diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index 0b3774e13bf3db05887c1d6f958d7831ac65892e..85a3a4e2337ad1c1fee9474ed0880748e4511270 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -195,7 +195,7 @@ namespace Canvas3D {
         const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper, handleHelper, {
             cameraHelper: p.camera.helper
         });
-        const pickPass = new PickPass(webgl, renderer, scene, camera, handleHelper, 0.5);
+        const pickPass = new PickPass(webgl, renderer, scene, camera, handleHelper, 0.25, drawPass);
         const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing);
         const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample);
 
diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts
index 8940f2e7725675b06baf32a160f27b8fdc908fb9..fc123fb77a79f5bb6c5b70c900cb61a6bae34f38 100644
--- a/src/mol-canvas3d/passes/draw.ts
+++ b/src/mol-canvas3d/passes/draw.ts
@@ -58,16 +58,16 @@ export const DefaultDrawPassProps = PD.getDefaultValues(DrawPassParams);
 export type DrawPassProps = PD.Values<typeof DrawPassParams>
 
 export class DrawPass {
-    colorTarget: RenderTarget
-    depthTexture: Texture
-    packedDepth: boolean
+    readonly colorTarget: RenderTarget
+    readonly depthTexture: Texture
+    readonly depthTexturePrimitives: Texture
+    readonly packedDepth: boolean
 
-    cameraHelper: CameraHelper
+    readonly cameraHelper: CameraHelper
 
     private depthTarget: RenderTarget
     private depthTargetPrimitives: RenderTarget | null
     private depthTargetVolumes: RenderTarget | null
-    private depthTexturePrimitives: Texture
     private depthTextureVolumes: Texture
     private depthMerge: DepthMergeRenderable
 
diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts
index 425c07544ce563ce360a4107ad36c86ea709b31c..51538baf255b8be05fa624903f7a5eaec0737cf2 100644
--- a/src/mol-canvas3d/passes/pick.ts
+++ b/src/mol-canvas3d/passes/pick.ts
@@ -12,6 +12,7 @@ import { PickingId } from '../../mol-geo/geometry/picking';
 import { decodeFloatRGB } from '../../mol-util/float-packing';
 import { Camera } from '../camera';
 import { HandleHelper } from '../helper/handle-helper';
+import { DrawPass } from './draw';
 
 const NullId = Math.pow(2, 24) - 2;
 
@@ -30,14 +31,14 @@ export class PickPass {
     private pickWidth: number
     private pickHeight: number
 
-    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number) {
+    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private handleHelper: HandleHelper, private pickBaseScale: number, private drawPass: DrawPass) {
         const { gl } = webgl;
         const width = gl.drawingBufferWidth;
         const height = gl.drawingBufferHeight;
 
         this.pickScale = pickBaseScale / webgl.pixelRatio;
-        this.pickWidth = Math.round(width * this.pickScale);
-        this.pickHeight = Math.round(height * this.pickScale);
+        this.pickWidth = Math.ceil(width * this.pickScale);
+        this.pickHeight = Math.ceil(height * this.pickScale);
 
         this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
         this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
@@ -57,8 +58,8 @@ export class PickPass {
 
     setSize(width: number, height: number) {
         this.pickScale = this.pickBaseScale / this.webgl.pixelRatio;
-        this.pickWidth = Math.round(width * this.pickScale);
-        this.pickHeight = Math.round(height * this.pickScale);
+        this.pickWidth = Math.ceil(width * this.pickScale);
+        this.pickHeight = Math.ceil(height * this.pickScale);
 
         this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
         this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
@@ -69,16 +70,22 @@ export class PickPass {
 
     render() {
         const { renderer, scene, camera, handleHelper: { scene: handleScene } } = this;
+        const depth = this.drawPass.depthTexturePrimitives;
         renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
 
         this.objectPickTarget.bind();
-        renderer.render(scene, camera, 'pickObject', true, false, null);
+        renderer.render(scene.primitives, camera, 'pickObject', true, false, null);
+        renderer.render(scene.volumes, camera, 'pickObject', false, false, depth);
         renderer.render(handleScene, camera, 'pickObject', false, false, null);
+
         this.instancePickTarget.bind();
-        renderer.render(scene, camera, 'pickInstance', true, false, null);
+        renderer.render(scene.primitives, camera, 'pickInstance', true, false, null);
+        renderer.render(scene.volumes, camera, 'pickInstance', false, false, depth);
         renderer.render(handleScene, camera, 'pickInstance', false, false, null);
+
         this.groupPickTarget.bind();
-        renderer.render(scene, camera, 'pickGroup', true, false, null);
+        renderer.render(scene.primitives, camera, 'pickGroup', true, false, null);
+        renderer.render(scene.volumes, camera, 'pickGroup', false, false, depth);
         renderer.render(handleScene, camera, 'pickGroup', false, false, null);
 
         this.pickDirty = false;
@@ -116,18 +123,21 @@ export class PickPass {
         y *= webgl.pixelRatio;
         y = gl.drawingBufferHeight - y; // flip y
 
-        const xp = Math.round(x * pickScale);
-        const yp = Math.round(y * pickScale);
+        const xp = Math.floor(x * pickScale);
+        const yp = Math.floor(y * pickScale);
 
         const objectId = this.getId(xp, yp, this.objectBuffer);
+        // console.log('objectId', objectId);
         if (objectId === -1 || objectId === NullId) return;
 
         const instanceId = this.getId(xp, yp, this.instanceBuffer);
+        // console.log('instanceId', instanceId);
         if (instanceId === -1 || instanceId === NullId) return;
 
         const groupId = this.getId(xp, yp, this.groupBuffer);
+        // console.log('groupId', groupId);
         if (groupId === -1 || groupId === NullId) return;
-
+        // console.log({ objectId, instanceId, groupId });
         return { objectId, instanceId, groupId };
     }
 }
\ No newline at end of file
diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts
index 198eb48124aaea070b6cbd609700159ef0fdc31e..cd3e99c97a6d7dbe57b65ec79c37d2a5d70eddf7 100644
--- a/src/mol-geo/geometry/direct-volume/direct-volume.ts
+++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts
@@ -133,9 +133,9 @@ export namespace DirectVolume {
         return {} as DirectVolume; // TODO
     }
 
-    export function createRenderModeParam(volume?: Volume) {
-        const isoValueParam = volume
-            ? Volume.createIsoValueParam(Volume.IsoValue.relative(2), volume.grid.stats)
+    export function createRenderModeParam(stats?: Grid['stats']) {
+        const isoValueParam = stats
+            ? Volume.createIsoValueParam(Volume.IsoValue.relative(2), stats)
             : Volume.IsoValueParam;
 
         return PD.MappedStatic('volume', {
diff --git a/src/mol-geo/geometry/image/image.ts b/src/mol-geo/geometry/image/image.ts
index 0283ee669e504cb72953f33e3dd37b50a3246929..0f34aeff787b93d7e900291fecd77e1de00592df 100644
--- a/src/mol-geo/geometry/image/image.ts
+++ b/src/mol-geo/geometry/image/image.ts
@@ -53,17 +53,17 @@ export { Image };
 interface Image {
     readonly kind: 'image',
 
-    readonly imageTexture: ValueCell<TextureImage<Float32Array>>,
+    readonly imageTexture: ValueCell<TextureImage<Uint8Array>>,
     readonly imageTextureDim: ValueCell<Vec2>,
     readonly cornerBuffer: ValueCell<Float32Array>,
-    readonly groupTexture: ValueCell<TextureImage<Float32Array>>,
+    readonly groupTexture: ValueCell<TextureImage<Uint8Array>>,
 
     /** Bounding sphere of the image */
     boundingSphere: Sphere3D
 }
 
 namespace Image {
-    export function create(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image?: Image): Image {
+    export function create(imageTexture: TextureImage<Uint8Array>, corners: Float32Array, groupTexture: TextureImage<Uint8Array>, image?: Image): Image {
         return image ?
             update(imageTexture, corners, groupTexture, image) :
             fromData(imageTexture, corners, groupTexture);
@@ -75,7 +75,7 @@ namespace Image {
         ]);
     }
 
-    function fromData(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>): Image {
+    function fromData(imageTexture: TextureImage<Uint8Array>, corners: Float32Array, groupTexture: TextureImage<Uint8Array>): Image {
         const boundingSphere = Sphere3D();
         let currentHash = -1;
 
@@ -101,7 +101,7 @@ namespace Image {
         return image;
     }
 
-    function update(imageTexture: TextureImage<Float32Array>, corners: Float32Array, groupTexture: TextureImage<Float32Array>, image: Image): Image {
+    function update(imageTexture: TextureImage<Uint8Array>, corners: Float32Array, groupTexture: TextureImage<Uint8Array>, image: Image): Image {
         const width = imageTexture.width;
         const height = imageTexture.height;
 
diff --git a/src/mol-gl/renderable/image.ts b/src/mol-gl/renderable/image.ts
index 90dcb2730384731353682bd989aa6843cff37563..0ba595cca0e1d49c1cc73c7cba7964664b8cc6a2 100644
--- a/src/mol-gl/renderable/image.ts
+++ b/src/mol-gl/renderable/image.ts
@@ -21,8 +21,8 @@ export const ImageSchema = {
     elements: ElementsSpec('uint32'),
 
     uImageTexDim: UniformSpec('v2'),
-    tImageTex: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
-    tGroupTex: TextureSpec('image-float32', 'alpha', 'float', 'nearest'),
+    tImageTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
+    tGroupTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
 
     dInterpolation: DefineSpec('string', InterpolationTypeNames),
 };
diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts
index d5569c154dfdf7b13d7b8a75394f4b5a0944c22d..4d392f4d1b5e8b055a70648e0a26c4d49468872c 100644
--- a/src/mol-gl/shader/direct-volume.frag.ts
+++ b/src/mol-gl/shader/direct-volume.frag.ts
@@ -267,8 +267,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                     #include apply_marker_color
                     #include apply_fog
 
-                    src.rgb = gl_FragColor.rgb;
-                    src.a =  gl_FragColor.a;
+                    src = gl_FragColor;
 
                     src.rgb *= src.a;
                     dst = (1.0 - dst.a) * src + dst; // standard blending
@@ -279,19 +278,14 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                 #endif
             }
             prevValue = value;
-        #endif
-
-        #if defined(dRenderMode_volume)
+        #elif defined(dRenderMode_volume)
             isoPos = toUnit(pos);
             vec4 mvPosition = uModelView * uTransform * vec4(isoPos * uGridDim, 1.0);
             if (calcDepth(mvPosition.xyz) > getDepth(gl_FragCoord.xy / uViewport.zw))
                 break;
 
-            // bool flipped = value > uIsoValue.y; // negative isosurfaces
-            // interior = value < uIsoValue.x && flipped;
             vec3 vViewPosition = mvPosition.xyz;
             vec4 material = transferFunction(value);
-            src.a = material.a * uAlpha;
 
             if (material.a >= 0.01) {
                 #ifdef dPackedGroup
@@ -304,14 +298,16 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
                 #endif
                 mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * uUnitToCartn)));
                 vec3 normal = -normalize(normalMatrix * normalize(gradient));
-                // normal = normal * (float(flipped) * 2.0 - 1.0);
-                // normal = normal * -(float(interior) * 2.0 - 1.0);
                 #include apply_light_color
-                src.rgb = gl_FragColor.rgb;
             } else {
-                src.rgb = material.rgb;
+                gl_FragColor.rgb = material.rgb;
             }
 
+            gl_FragColor.a = material.a * uAlpha;
+            #include apply_fog
+
+            src = gl_FragColor;
+
             src.rgb *= src.a;
             dst = (1.0 - dst.a) * src + dst; // standard blending
         #endif
@@ -322,13 +318,20 @@ vec4 raymarch(vec3 startLoc, vec3 step) {
 
         pos += step;
     }
+
+    #if defined(dRenderMode_isosurface) && defined(enabledFragDepth)
+        // ensure depth is written everywhere
+        if (!hit)
+            gl_FragDepthEXT = 1.0;
+    #endif
+
     return dst;
 }
 
 // TODO calculate normalMatrix on CPU
 // TODO fix near/far clipping
 // TODO support clip objects
-// TODO support float texture for higher precision values
+// TODO support float texture for higher precision values???
 
 void main () {
     // TODO handle on CPU in renderloop
@@ -349,7 +352,5 @@ void main () {
     vec3 step = rayDir * stepScale;
 
     gl_FragColor = raymarch(origPos, step);
-    if (length(gl_FragColor.rgb) < 0.00001)
-        discard;
 }
 `;
\ No newline at end of file
diff --git a/src/mol-gl/shader/gaussian-density.frag.ts b/src/mol-gl/shader/gaussian-density.frag.ts
index 6f9d31b866d18c26049a9036f22ea4b5a923f6fb..b6b05b91e23080791f11e49a6519aa165780bd4f 100644
--- a/src/mol-gl/shader/gaussian-density.frag.ts
+++ b/src/mol-gl/shader/gaussian-density.frag.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -11,9 +11,14 @@ precision highp float;
 varying vec3 vPosition;
 varying float vRadiusSqInv;
 #if defined(dCalcType_groupId)
-    precision highp sampler2D;
-    uniform sampler2D tMinDistanceTex;
-    uniform vec3 uGridTexDim;
+    #if defined(dGridTexType_2d)
+        precision highp sampler2D;
+        uniform sampler2D tMinDistanceTex;
+        uniform vec3 uGridTexDim;
+    #elif defined(dGridTexType_3d)
+        precision highp sampler3D;
+        uniform sampler3D tMinDistanceTex;
+    #endif
     varying float vGroup;
 #endif
 
@@ -26,6 +31,7 @@ uniform float uCurrentX;
 uniform float uCurrentY;
 uniform float uAlpha;
 uniform float uResolution;
+uniform float uRadiusFactor;
 
 void main() {
     vec2 v = gl_FragCoord.xy - vec2(uCurrentX, uCurrentY) - 0.5;
@@ -34,12 +40,16 @@ void main() {
 
     #if defined(dCalcType_density)
         float density = exp(-uAlpha * ((dist * dist) * vRadiusSqInv));
-        gl_FragColor.a = density;
+        gl_FragColor.a = density / uRadiusFactor;
     #elif defined(dCalcType_minDistance)
-        gl_FragColor.a = 10000.0 - dist;
+        gl_FragColor.a = 1.0 - dist / uRadiusFactor;
     #elif defined(dCalcType_groupId)
-        float minDistance = 10000.0 - texture2D(tMinDistanceTex, (gl_FragCoord.xy) / (uGridTexDim.xy / uGridTexScale)).a;
-        if (dist > minDistance + uResolution * 0.05)
+        #if defined(dGridTexType_2d)
+            float minDistance = 1.0 - texture2D(tMinDistanceTex, (gl_FragCoord.xy) / (uGridTexDim.xy / uGridTexScale)).a;
+        #elif defined(dGridTexType_3d)
+            float minDistance = 1.0 - texelFetch(tMinDistanceTex, ivec3(gl_FragCoord.xy, uCurrentSlice), 0).a;
+        #endif
+        if (dist / uRadiusFactor > minDistance + uResolution * 0.05)
             discard;
         gl_FragColor.rgb = encodeFloatRGB(vGroup);
     #endif
diff --git a/src/mol-gl/shader/image.frag.ts b/src/mol-gl/shader/image.frag.ts
index 11e7acbd4dfa38e5c60e0a29c2df55e4486e6618..3b2e076d4d530c30b7ad7e16a8b4100e4f85ae4c 100644
--- a/src/mol-gl/shader/image.frag.ts
+++ b/src/mol-gl/shader/image.frag.ts
@@ -104,8 +104,7 @@ void main() {
         #elif defined(dRenderVariant_pickInstance)
             gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
         #elif defined(dRenderVariant_pickGroup)
-            float group = texture2D(tGroupTex, vUv).a;
-            gl_FragColor = vec4(encodeFloatRGB(group), 1.0);
+            gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
         #endif
     #elif defined(dRenderVariant_depth)
         if (imageData.a < 0.05)
@@ -119,7 +118,7 @@ void main() {
         gl_FragColor = imageData;
         gl_FragColor.a *= uAlpha;
 
-        float group = texture2D(tGroupTex, vUv).a;
+        float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
         float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
         #include apply_marker_color
         #include apply_fog
diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts
index f0f8205a37ec7f1f9e11a40dead7812942784eef..ce0976ed28e1066707d3fa85173a213379d6ccc6 100644
--- a/src/mol-gl/webgl/texture.ts
+++ b/src/mol-gl/webgl/texture.ts
@@ -219,6 +219,7 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
             width = data.width, height = data.height;
             gl.texImage2D(target, 0, internalFormat, width, height, 0, format, type, data.array);
         } else if (isWebGL2(gl) && isTexture3d(data, target, gl)) {
+            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
             width = data.width, height = data.height, depth = data.depth;
             gl.texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, data.array);
         } else {
diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts
index 61204ab8160a841379e3dfd07907a60d22376f4c..c91bca94147082cc4bd85835af0ea311498e535f 100644
--- a/src/mol-math/geometry/gaussian-density/gpu.ts
+++ b/src/mol-math/geometry/gaussian-density/gpu.ts
@@ -20,6 +20,7 @@ import { createComputeRenderItem } from '../../../mol-gl/webgl/render-item';
 import { ValueSpec, AttributeSpec, UniformSpec, TextureSpec, DefineSpec, Values } from '../../../mol-gl/renderable/schema';
 import gaussian_density_vert from '../../../mol-gl/shader/gaussian-density.vert';
 import gaussian_density_frag from '../../../mol-gl/shader/gaussian-density.frag';
+import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
 
 export const GaussianDensitySchema = {
     drawCount: ValueSpec('number'),
@@ -39,22 +40,30 @@ export const GaussianDensitySchema = {
     uGridTexScale: UniformSpec('v2', true),
     uAlpha: UniformSpec('f', true),
     uResolution: UniformSpec('f', true),
+    uRadiusFactor: UniformSpec('f', true),
     tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),
     dCalcType: DefineSpec('string', ['density', 'minDistance', 'groupId']),
 };
+type GaussianDensityValues = Values<typeof GaussianDensitySchema>
+type GaussianDensityRenderable = ComputeRenderable<GaussianDensityValues>
 
 export const GaussianDensityShaderCode = ShaderCode(
     'gaussian-density', gaussian_density_vert, gaussian_density_frag,
     { standardDerivatives: false, fragDepth: false }
 );
 
+let _tmpTexture: Texture | undefined = undefined;
+
 export function GaussianDensityGPU(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, webgl: WebGLContext): DensityData {
     // always use texture2d when the gaussian density needs to be downloaded from the GPU,
     // it's faster than texture3d
     // console.time('GaussianDensityTexture2d')
-    const { scale, bbox, texture, gridDim, gridTexDim } = calcGaussianDensityTexture2d(webgl, position, box, radius, props);
+    if (!_tmpTexture) {
+        _tmpTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
+    }
+    const { scale, bbox, texture, gridDim, gridTexDim } = calcGaussianDensityTexture2d(webgl, position, box, radius, props, _tmpTexture);
     // webgl.waitForGpuCommandsCompleteSync()
     // console.timeEnd('GaussianDensityTexture2d')
     const { field, idField } = fieldFromTexture2d(webgl, texture, gridDim, gridTexDim);
@@ -98,35 +107,45 @@ type GaussianDensityTextureData = {
     gridTexScale: Vec2
 }
 
+let _tmpFramebuffer: Framebuffer | undefined = undefined;
+let _minDistanceTexture2d: Texture | undefined = undefined;
+
 function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): GaussianDensityTextureData {
-    const { smoothness } = props;
+    // console.log('2d');
+    const { smoothness, resolution } = props;
 
-    const { drawCount, positions, radii, groups, scale, expandedBox, dim } = prepareGaussianDensityData(position, box, radius, props);
+    const { drawCount, positions, radii, groups, scale, expandedBox, dim, maxRadius } = prepareGaussianDensityData(position, box, radius, props);
     const [ dx, dy, dz ] = dim;
-    const { texDimX, texDimY, texCols, powerOfTwoSize } = getTexture2dSize(dim);
-    // console.log({ texDimX, texDimY, texCols, powerOfTwoSize, dim })
+    const { texDimX, texDimY, texCols } = getTexture2dSize(dim);
+    // console.log({ texDimX, texDimY, texCols, texSize, dim });
     const gridTexDim = Vec3.create(texDimX, texDimY, 0);
-    const gridTexScale = Vec2.create(texDimX / powerOfTwoSize, texDimY / powerOfTwoSize);
-
-    const minDistanceTexture = webgl.resources.texture('image-float32', 'rgba', 'float', 'nearest');
-    minDistanceTexture.define(powerOfTwoSize, powerOfTwoSize);
+    const gridTexScale = Vec2.create(texDimX / texDimX, texDimY / texDimY);
+    const radiusFactor = maxRadius * 2;
+
+    if (!_minDistanceTexture2d) {
+        _minDistanceTexture2d = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+        _minDistanceTexture2d.define(texDimX, texDimY);
+    } else if (_minDistanceTexture2d.getWidth() !== texDimX || _minDistanceTexture2d.getHeight() !== texDimY) {
+        _minDistanceTexture2d.define(texDimX, texDimY);
+    }
 
-    const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, gridTexDim, gridTexScale, smoothness, props.resolution);
+    const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, _minDistanceTexture2d, expandedBox, dim, gridTexDim, gridTexScale, smoothness, resolution, radiusFactor);
 
     //
 
     const { gl, resources, state } = webgl;
     const { uCurrentSlice, uCurrentX, uCurrentY } = renderable.values;
 
-    const framebuffer = resources.framebuffer();
+    if (!_tmpFramebuffer) _tmpFramebuffer = resources.framebuffer();
+    const framebuffer = _tmpFramebuffer;
     framebuffer.bind();
     setRenderingDefaults(webgl);
 
     if (!texture) {
-        texture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
-        texture.define(powerOfTwoSize, powerOfTwoSize);
-    } else if (texture.getWidth() !== powerOfTwoSize || texture.getHeight() !== powerOfTwoSize) {
-        texture.define(powerOfTwoSize, powerOfTwoSize);
+        texture = resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
+        texture.define(texDimX, texDimY);
+    } else if (texture.getWidth() !== texDimX || texture.getHeight() !== texDimY) {
+        texture.define(texDimX, texDimY);
     }
 
     // console.log(renderable)
@@ -160,28 +179,37 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
     render(texture, true);
 
     setupMinDistanceRendering(webgl, renderable);
-    render(minDistanceTexture, true);
+    render(_minDistanceTexture2d, true);
 
     setupGroupIdRendering(webgl, renderable);
     render(texture, false);
 
-    // printTexture(webgl, texture, 1)
+    // printTexture(webgl, texture, 1);
 
     return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale };
 }
 
+let _minDistanceTexture3d: Texture | undefined = undefined;
+
 function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityGPUProps, texture?: Texture): GaussianDensityTextureData {
-    const { gl, resources } = webgl;
-    const { smoothness } = props;
+    // console.log('3d');
+    const { gl, resources, state } = webgl;
+    const { smoothness, resolution } = props;
 
-    const { drawCount, positions, radii, groups, scale, expandedBox, dim } = prepareGaussianDensityData(position, box, radius, props);
+    const { drawCount, positions, radii, groups, scale, expandedBox, dim, maxRadius } = prepareGaussianDensityData(position, box, radius, props);
     const [ dx, dy, dz ] = dim;
-    const minDistanceTexture = resources.texture('volume-float32', 'rgba', 'float', 'nearest');
-    minDistanceTexture.define(dx, dy, dz);
+
+    if (!_minDistanceTexture3d) {
+        _minDistanceTexture3d = resources.texture('volume-uint8', 'rgba', 'ubyte', 'nearest');
+        _minDistanceTexture3d.define(dx, dy, dz);
+    } else if (_minDistanceTexture3d.getWidth() !== dx || _minDistanceTexture3d.getHeight() !== dy || _minDistanceTexture3d.getDepth() !== dz) {
+        _minDistanceTexture3d.define(dx, dy, dz);
+    }
 
     const gridTexScale = Vec2.create(1, 1);
+    const radiusFactor = maxRadius * 2;
 
-    const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistanceTexture, expandedBox, dim, dim, gridTexScale, smoothness, props.resolution);
+    const renderable = getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, _minDistanceTexture3d, expandedBox, dim, dim, gridTexScale, smoothness, resolution, radiusFactor);
 
     //
 
@@ -192,25 +220,32 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
     setRenderingDefaults(webgl);
     gl.viewport(0, 0, dx, dy);
 
-    if (!texture) texture = resources.texture('volume-float32', 'rgba', 'float', 'nearest');
-    texture.define(dx, dy, dz);
+    if (!texture) {
+        texture = resources.texture('volume-uint8', 'rgba', 'ubyte', 'linear');
+        texture.define(dx, dy, dz);
+    } else if (texture.getWidth() !== dx || texture.getHeight() !== dy || texture.getDepth() !== dz) {
+        texture.define(dx, dy, dz);
+    }
 
-    function render(fbTex: Texture) {
+    function render(fbTex: Texture, clear: boolean) {
+        state.currentRenderItemId = -1;
         for (let i = 0; i < dz; ++i) {
             ValueCell.update(uCurrentSlice, i);
             fbTex.attachFramebuffer(framebuffer, 0, i);
+            if (clear) gl.clear(gl.COLOR_BUFFER_BIT);
             renderable.render();
         }
+        gl.finish();
     }
 
-    setupMinDistanceRendering(webgl, renderable);
-    render(minDistanceTexture);
-
     setupDensityRendering(webgl, renderable);
-    render(texture);
+    render(texture, true);
+
+    setupMinDistanceRendering(webgl, renderable);
+    render(_minDistanceTexture3d, true);
 
     setupGroupIdRendering(webgl, renderable);
-    render(texture);
+    render(texture, false);
 
     return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridTexScale };
 }
@@ -250,13 +285,51 @@ function prepareGaussianDensityData(position: PositionData, box: Box3D, radius:
 
     const scale = Vec3.create(resolution, resolution, resolution);
 
-    return { drawCount: n, positions, radii, groups, scale, expandedBox, dim };
+    return { drawCount: n, positions, radii, groups, scale, expandedBox, dim, maxRadius };
+}
+
+let _GaussianDensityRenderable: GaussianDensityRenderable | undefined = undefined;
+
+function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, smoothness: number, resolution: number, radiusFactor: number) {
+    // console.log('radiusFactor', radiusFactor);
+    if (_GaussianDensityRenderable) {
+        const extent = Vec3.sub(Vec3(), box.max, box.min);
+        const v = _GaussianDensityRenderable.values;
+
+        ValueCell.updateIfChanged(v.drawCount, drawCount);
+        ValueCell.updateIfChanged(v.instanceCount, 1);
+
+        ValueCell.updateIfChanged(v.aRadius, radii);
+        ValueCell.updateIfChanged(v.aPosition, positions);
+        ValueCell.updateIfChanged(v.aGroup, groups);
+
+        ValueCell.updateIfChanged(v.uCurrentSlice, 0);
+        ValueCell.updateIfChanged(v.uCurrentX, 0);
+        ValueCell.updateIfChanged(v.uCurrentY, 0);
+        ValueCell.updateIfChanged(v.uBboxMin, box.min);
+        ValueCell.updateIfChanged(v.uBboxSize, extent);
+        ValueCell.updateIfChanged(v.uGridDim, gridDim);
+        ValueCell.updateIfChanged(v.uGridTexDim, gridTexDim);
+        ValueCell.updateIfChanged(v.uGridTexScale, gridTexScale);
+        ValueCell.updateIfChanged(v.uAlpha, smoothness);
+        ValueCell.updateIfChanged(v.uResolution, resolution);
+        ValueCell.updateIfChanged(v.uRadiusFactor, radiusFactor);
+        ValueCell.updateIfChanged(v.tMinDistanceTex, minDistanceTexture);
+
+        ValueCell.updateIfChanged(v.dGridTexType, minDistanceTexture.getDepth() > 0 ? '3d' : '2d');
+        ValueCell.updateIfChanged(v.dCalcType, 'density');
+
+        _GaussianDensityRenderable.update();
+    } else {
+        _GaussianDensityRenderable = _getGaussianDensityRenderable(webgl, drawCount, positions, radii, groups, minDistanceTexture, box, gridDim, gridTexDim, gridTexScale, smoothness, resolution, radiusFactor);
+    }
+    return _GaussianDensityRenderable;
 }
 
-function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, smoothness: number, resolution: number) {
-    const extent = Vec3.sub(Vec3.zero(), box.max, box.min);
+function _getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, positions: Float32Array, radii: Float32Array, groups: Float32Array, minDistanceTexture: Texture, box: Box3D, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, smoothness: number, resolution: number, radiusFactor: number) {
+    const extent = Vec3.sub(Vec3(), box.max, box.min);
 
-    const values: Values<typeof GaussianDensitySchema> = {
+    const values: GaussianDensityValues = {
         drawCount: ValueCell.create(drawCount),
         instanceCount: ValueCell.create(1),
 
@@ -274,10 +347,11 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po
         uGridTexScale: ValueCell.create(gridTexScale),
         uAlpha: ValueCell.create(smoothness),
         uResolution: ValueCell.create(resolution),
+        uRadiusFactor: ValueCell.create(radiusFactor),
         tMinDistanceTex: ValueCell.create(minDistanceTexture),
 
         dGridTexType: ValueCell.create(minDistanceTexture.getDepth() > 0 ? '3d' : '2d'),
-        dCalcType: ValueCell.create('minDistance'),
+        dCalcType: ValueCell.create('density'),
     };
 
     const schema = { ...GaussianDensitySchema };
@@ -297,7 +371,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.clearColor(0, 0, 0, 0);
 }
 
-function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) {
+function setupMinDistanceRendering(webgl: WebGLContext, renderable: GaussianDensityRenderable) {
     const { gl, state } = webgl;
     ValueCell.update(renderable.values.dCalcType, 'minDistance');
     renderable.update();
@@ -310,18 +384,16 @@ function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRende
     state.blendEquation(webgl.extensions.blendMinMax.MAX);
 }
 
-function setupDensityRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) {
+function setupDensityRendering(webgl: WebGLContext, renderable: GaussianDensityRenderable) {
     const { gl, state } = webgl;
     ValueCell.update(renderable.values.dCalcType, 'density');
     renderable.update();
     state.colorMask(false, false, false, true);
     state.blendFunc(gl.ONE, gl.ONE);
-    // state.colorMask(true, true, true, true)
-    // state.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ONE)
     state.blendEquation(gl.FUNC_ADD);
 }
 
-function setupGroupIdRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) {
+function setupGroupIdRendering(webgl: WebGLContext, renderable: GaussianDensityRenderable) {
     const { gl, state } = webgl;
     ValueCell.update(renderable.values.dCalcType, 'groupId');
     renderable.update();
@@ -348,14 +420,13 @@ function getTexture2dSize(gridDim: Vec3) {
     } else {
         texDimX = gridDim[0] * gridDim[2];
     }
-    return { texDimX, texDimY, texRows, texCols, powerOfTwoSize: texDimY < powerOfTwoSize ? powerOfTwoSize : powerOfTwoSize * 2 };
+    return { texDimX, texDimY, texRows, texCols, powerOfTwoSize: texDimY };
 }
 
 export function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec3, texDim: Vec3) {
     // console.time('fieldFromTexture2d')
     const { resources } = ctx;
     const [ dx, dy, dz ] = dim;
-    // const { width, height } = texture
     const [ width, height ] = texDim;
     const fboTexCols = Math.floor(width / dx);
 
@@ -365,8 +436,7 @@ export function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec
     const idData = space.create();
     const idField = Tensor.create(space, idData);
 
-    // const image = new Uint8Array(width * height * 4)
-    const image = new Float32Array(width * height * 4);
+    const image = new Uint8Array(width * height * 4);
 
     const framebuffer = resources.framebuffer();
     framebuffer.bind();
@@ -386,8 +456,8 @@ export function fieldFromTexture2d(ctx: WebGLContext, texture: Texture, dim: Vec
         for (let iy = 0; iy < dy; ++iy) {
             for (let ix = 0; ix < dx; ++ix) {
                 const idx = 4 * (tmpCol * dx + (iy + tmpRow) * width + ix);
-                data[j] = image[idx + 3]; // / 255
-                idData[j] = decodeFloatRGB(image[idx] * 255, image[idx + 1] * 255, image[idx + 2] * 255);
+                data[j] = image[idx + 3]  / 255;
+                idData[j] = decodeFloatRGB(image[idx], image[idx + 1], image[idx + 2]);
                 j++;
             }
         }
diff --git a/src/mol-repr/structure/registry.ts b/src/mol-repr/structure/registry.ts
index 03e0c3af3f70b439b4c659f9c2dbc315a96e2781..f49e9b37fa1e13b2d26f24f6a1b358e0f144fd2a 100644
--- a/src/mol-repr/structure/registry.ts
+++ b/src/mol-repr/structure/registry.ts
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -20,6 +20,7 @@ import { PointRepresentationProvider } from './representation/point';
 import { PuttyRepresentationProvider } from './representation/putty';
 import { SpacefillRepresentationProvider } from './representation/spacefill';
 import { LineRepresentationProvider } from './representation/line';
+import { GaussianVolumeRepresentationProvider } from './representation/gaussian-volume';
 
 export class StructureRepresentationRegistry extends RepresentationRegistry<Structure, StructureRepresentationState> {
     constructor() {
@@ -38,7 +39,7 @@ export namespace StructureRepresentationRegistry {
         'carbohydrate': CarbohydrateRepresentationProvider,
         'ellipsoid': EllipsoidRepresentationProvider,
         'gaussian-surface': GaussianSurfaceRepresentationProvider,
-        // 'gaussian-volume': GaussianVolumeRepresentationProvider, // TODO disabled for now, needs more work
+        'gaussian-volume': GaussianVolumeRepresentationProvider, // TODO disabled for now, needs more work
         'label': LabelRepresentationProvider,
         'line': LineRepresentationProvider,
         'molecular-surface': MolecularSurfaceRepresentationProvider,
diff --git a/src/mol-repr/structure/representation/gaussian-volume.ts b/src/mol-repr/structure/representation/gaussian-volume.ts
index 3c98dea41b55e7fda48ee00a0f9a7ea85654862e..eb2439e584ec3c65ebec9a603caf08ee96fd54b6 100644
--- a/src/mol-repr/structure/representation/gaussian-volume.ts
+++ b/src/mol-repr/structure/representation/gaussian-volume.ts
@@ -10,6 +10,7 @@ import { StructureRepresentation, StructureRepresentationProvider, ComplexRepres
 import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
 import { ThemeRegistryContext } from '../../../mol-theme/theme';
 import { Structure } from '../../../mol-model/structure';
+import { DirectVolume } from '../../../mol-geo/geometry/direct-volume/direct-volume';
 
 const GaussianVolumeVisuals = {
     'gaussian-volume': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianDensityVolumeParams>) => ComplexRepresentation('Gaussian volume', ctx, getParams, GaussianDensityVolumeVisual)
@@ -20,7 +21,12 @@ export const GaussianVolumeParams = {
 };
 export type GaussianVolumeParams = typeof GaussianVolumeParams
 export function getGaussianVolumeParams(ctx: ThemeRegistryContext, structure: Structure) {
-    return PD.clone(GaussianVolumeParams);
+    const p = PD.clone(GaussianVolumeParams);
+    p.renderMode = DirectVolume.createRenderModeParam({
+        // TODO find a better way to set
+        min: 0, max: 1, mean: 0.04, sigma: 0.01
+    });
+    return p;
 }
 
 export type GaussianVolumeRepresentation = StructureRepresentation<GaussianVolumeParams>
diff --git a/src/mol-repr/structure/visual/gaussian-density-volume.ts b/src/mol-repr/structure/visual/gaussian-density-volume.ts
index cdb7e5e66f129a86a4dc75d6a2838466a6f06446..3cdfdfb0602bc1b06098f40ac0216c0d2ebd9d21 100644
--- a/src/mol-repr/structure/visual/gaussian-density-volume.ts
+++ b/src/mol-repr/structure/visual/gaussian-density-volume.ts
@@ -14,6 +14,7 @@ import { ComplexDirectVolumeParams, ComplexVisual, ComplexDirectVolumeVisual } f
 import { VisualUpdateState } from '../../util';
 import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
 import { eachSerialElement, ElementIterator, getSerialElementLoci } from './util/element';
+import { Sphere3D } from '../../../mol-math/geometry';
 
 async function createGaussianDensityVolume(ctx: VisualContext, structure: Structure, theme: Theme, props: GaussianDensityTextureProps, directVolume?: DirectVolume): Promise<DirectVolume> {
     const { runtime, webgl } = ctx;
@@ -23,12 +24,17 @@ async function createGaussianDensityVolume(ctx: VisualContext, structure: Struct
     const oldTexture = directVolume ? directVolume.gridTexture.ref.value : undefined;
     const densityTextureData = await computeStructureGaussianDensityTexture(structure, p, webgl, oldTexture).runInContext(runtime);
     const { transform, texture, bbox, gridDim } = densityTextureData;
-    const stats = { min: 0, max: 1, mean: 0.5, sigma: 0.1 };
+    const stats = { min: 0, max: 1, mean: 0.04, sigma: 0.01 };
 
     const unitToCartn = Mat4.mul(Mat4(), transform, Mat4.fromScaling(Mat4(), gridDim));
     const cellDim = Vec3.create(1, 1, 1);
 
-    return DirectVolume.create(bbox, gridDim, transform, unitToCartn, cellDim, texture, stats, true, directVolume);
+    const vol = DirectVolume.create(bbox, gridDim, transform, unitToCartn, cellDim, texture, stats, true, directVolume);
+
+    const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, props.radiusOffset);
+    vol.setBoundingSphere(sphere);
+
+    return vol;
 }
 
 export const GaussianDensityVolumeParams = {
diff --git a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
index 1eb19601781f6bcad2f75c3bce700abda96259d1..eb8f5bfab59f3a7d880bc2d11c9149f24be28910 100644
--- a/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
+++ b/src/mol-repr/structure/visual/gaussian-surface-mesh.ts
@@ -7,7 +7,6 @@
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { UnitsMeshParams, UnitsTextureMeshParams, UnitsVisual, UnitsMeshVisual, UnitsTextureMeshVisual } from '../units-visual';
 import { GaussianDensityParams, computeUnitGaussianDensity, GaussianDensityTextureProps, computeUnitGaussianDensityTexture2d, GaussianDensityProps, computeStructureGaussianDensity, getUnitExtraRadius } from './util/gaussian';
-import { WebGLContext } from '../../../mol-gl/webgl/context';
 import { VisualContext } from '../../visual';
 import { Unit, Structure } from '../../../mol-model/structure';
 import { Theme } from '../../../mol-theme/theme';
@@ -29,10 +28,6 @@ export const GaussianSurfaceMeshParams = {
 };
 export type GaussianSurfaceMeshParams = typeof GaussianSurfaceMeshParams
 
-export function getGaussianSurfaceVisual(webgl?: WebGLContext) {
-    return webgl && webgl.extensions.drawBuffers ? GaussianSurfaceTextureMeshVisual : GaussianSurfaceMeshVisual;
-}
-
 //
 
 async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
diff --git a/src/mol-repr/volume/direct-volume.ts b/src/mol-repr/volume/direct-volume.ts
index 552d5b94924bbb20a9b23e956885dce8ae34a00e..cd4626c705f6f19405e6bb3449bc64549893849c 100644
--- a/src/mol-repr/volume/direct-volume.ts
+++ b/src/mol-repr/volume/direct-volume.ts
@@ -239,7 +239,7 @@ export const DirectVolumeParams = {
 export type DirectVolumeParams = typeof DirectVolumeParams
 export function getDirectVolumeParams(ctx: ThemeRegistryContext, volume: Volume) {
     const p = PD.clone(DirectVolumeParams);
-    p.renderMode = DirectVolume.createRenderModeParam(volume);
+    p.renderMode = DirectVolume.createRenderModeParam(volume.grid.stats);
     return p;
 }
 export type DirectVolumeProps = PD.Values<DirectVolumeParams>
diff --git a/src/mol-repr/volume/slice.ts b/src/mol-repr/volume/slice.ts
index f1b3273f46ae05f8be1da54699d010652f2d6960..21309f24650691dde8f275e64ad48cf69c2e4a10 100644
--- a/src/mol-repr/volume/slice.ts
+++ b/src/mol-repr/volume/slice.ts
@@ -22,6 +22,7 @@ import { equalEps } from '../../mol-math/linear-algebra/3d/common';
 import { RenderableState } from '../../mol-gl/renderable';
 import { Color } from '../../mol-util/color';
 import { ColorTheme } from '../../mol-theme/color';
+import { encodeFloatRGBtoArray } from '../../mol-util/float-packing';
 
 export async function createImage(ctx: VisualContext, volume: Volume, theme: Theme, props: PD.Values<SliceParams>, image?: Image) {
     const { dimension: { name: dim }, isoValue } = props;
@@ -47,8 +48,8 @@ export async function createImage(ctx: VisualContext, volume: Volume, theme: The
                 [0, 0, z,  0, y, z,  x, 0, z,  x, y, z]
     );
 
-    const imageArray = new Float32Array(width * height * 4);
-    const groupArray = getGroupArray(volume.grid, props);
+    const imageArray = new Uint8Array(width * height * 4);
+    const groupArray = getPackedGroupArray(volume.grid, props);
 
     let i = 0;
     for (let iy = y0; iy < ny; ++iy) {
@@ -57,10 +58,10 @@ export async function createImage(ctx: VisualContext, volume: Volume, theme: The
                 const val = space.get(data, ix, iy, iz);
                 const normVal = (val - min) / (max - min);
 
-                imageArray[i] = r * normVal * 2;
-                imageArray[i + 1] = g * normVal * 2;
-                imageArray[i + 2] = b * normVal * 2;
-                imageArray[i + 3] = val >= isoVal ? 1 : 0;
+                imageArray[i] = r * normVal * 2 * 255;
+                imageArray[i + 1] = g * normVal * 2 * 255;
+                imageArray[i + 2] = b * normVal * 2 * 255;
+                imageArray[i + 3] = val >= isoVal ? 255 : 0;
 
                 i += 4;
             }
@@ -106,10 +107,27 @@ function getSliceInfo(grid: Grid, props: PD.Values<SliceParams>) {
     };
 }
 
+function getPackedGroupArray(grid: Grid, props: PD.Values<SliceParams>) {
+    const { space } = grid.cells;
+    const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(grid, props);
+    const groupArray = new Uint8Array(width * height * 4);
+
+    let j = 0;
+    for (let iy = y0; iy < ny; ++iy) {
+        for (let ix = x0; ix < nx; ++ix) {
+            for (let iz = z0; iz < nz; ++iz) {
+                encodeFloatRGBtoArray(space.dataOffset(ix, iy, iz), groupArray, j);
+                j += 4;
+            }
+        }
+    }
+    return groupArray;
+}
+
 function getGroupArray(grid: Grid, props: PD.Values<SliceParams>) {
     const { space } = grid.cells;
     const { width, height, x0, y0, z0, nx, ny, nz } = getSliceInfo(grid, props);
-    const groupArray = new Float32Array(width * height);
+    const groupArray = new Uint32Array(width * height);
 
     let j = 0;
     for (let iy = y0; iy < ny; ++iy) {