diff --git a/package-lock.json b/package-lock.json
index 1b6468e2567dbae6fc5e7f1fd17889ee8aceb881..ce603119b23d7d2f2eb7e9c8cb2c7c0421f37e54 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index d32b4991e31a481b1e98fcf1f0338858506e1599..358c5d3908dbd662aebdd9c4b1379d1d4d86aca6 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -208,19 +208,15 @@ namespace Canvas3D {
                     case 'pick':
                         renderer.setViewport(0, 0, pickWidth, pickHeight);
                         objectPickTarget.bind();
-                        renderer.clear()
                         renderer.render(scene, 'pickObject');
                         instancePickTarget.bind();
-                        renderer.clear()
                         renderer.render(scene, 'pickInstance');
                         groupPickTarget.bind();
-                        renderer.clear()
                         renderer.render(scene, 'pickGroup');
                         break;
                     case 'draw':
                         webgl.unbindFramebuffer();
                         renderer.setViewport(0, 0, canvas.width, canvas.height);
-                        renderer.clear()
                         renderer.render(scene, variant);
                         if (debugHelper.isEnabled) {
                             debugHelper.syncVisibility()
diff --git a/src/mol-geo/util/marching-cubes/algorithm.ts b/src/mol-geo/util/marching-cubes/algorithm.ts
index d2f524f2c308399cce8f598cd8622c51b007dba8..e446ad2576f1d9a3ffc2b8afda4c0ba2f87b8c2a 100644
--- a/src/mol-geo/util/marching-cubes/algorithm.ts
+++ b/src/mol-geo/util/marching-cubes/algorithm.ts
@@ -173,6 +173,8 @@ class MarchingCubesState {
         );
         this.verticesOnEdges[edgeId] = id + 1;
 
+        // TODO cache scalarField differences for slices
+        // TODO make calculation optional
         const n0x = sfg(sf, Math.max(0, li - 1), lj, lk) - sfg(sf, Math.min(this.nX - 1, li + 1), lj, lk)
         const n0y = sfg(sf, li, Math.max(0, lj - 1), lk) - sfg(sf, li, Math.min(this.nY - 1, lj + 1), lk)
         const n0z = sfg(sf, li, lj, Math.max(0, lk - 1)) - sfg(sf, li, lj, Math.min(this.nZ, lk + 1))
diff --git a/src/mol-gl/compute/histogram-pyramid/reduction.ts b/src/mol-gl/compute/histogram-pyramid/reduction.ts
index 2705e28666ac875b94aeba1b4fa002428d2bea8c..0a33038ec3db9b9c4386e5a679865559158ca72b 100644
--- a/src/mol-gl/compute/histogram-pyramid/reduction.ts
+++ b/src/mol-gl/compute/histogram-pyramid/reduction.ts
@@ -14,6 +14,7 @@ import { ValueCell } from 'mol-util';
 import { QuadSchema, QuadValues } from '../util';
 import { Vec2 } from 'mol-math/linear-algebra';
 import { getHistopyramidSum } from './sum';
+import { Framebuffer, createFramebuffer } from 'mol-gl/webgl/framebuffer';
 
 const HistopyramidReductionSchema = {
     ...QuadSchema,
@@ -61,12 +62,31 @@ function getLevelTexture(ctx: WebGLContext, level: number) {
     return tex
 }
 
+type TextureFramebuffer = { texture: Texture, framebuffer: Framebuffer }
+const LevelTexturesFramebuffers: TextureFramebuffer[] = []
+function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) {
+    let textureFramebuffer  = LevelTexturesFramebuffers[level]
+    const size = Math.pow(2, level)
+    if (textureFramebuffer === undefined) {
+        const texture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
+        const framebuffer = createFramebuffer(ctx.gl, ctx.stats)
+        texture.attachFramebuffer(framebuffer, 0)
+        textureFramebuffer = { texture, framebuffer }
+        textureFramebuffer.texture.define(size, size)
+        LevelTexturesFramebuffers[level] = textureFramebuffer
+    }
+    return textureFramebuffer
+}
+
 function setRenderingDefaults(ctx: WebGLContext) {
     const { gl, state } = ctx
     state.disable(gl.CULL_FACE)
     state.disable(gl.BLEND)
     state.disable(gl.DEPTH_TEST)
+    state.disable(gl.SCISSOR_TEST)
     state.depthMask(false)
+    state.colorMask(true, true, true, true)
+    state.clearColor(0, 0, 0, 0)
 }
 
 export interface HistogramPyramid {
@@ -98,16 +118,19 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture)
     const pyramidTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
     pyramidTexture.define(maxSize, maxSize)
 
-    const levelTextures: Texture[] = []
-    for (let i = 0; i < levels; ++i) levelTextures.push(getLevelTexture(ctx, i))
+    const levelTexturesFramebuffers: TextureFramebuffer[] = []
+    for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i))
 
     const renderable = getHistopyramidReductionRenderable(ctx, initialTexture)
+    ctx.state.currentRenderItemId = -1
     setRenderingDefaults(ctx)
 
     let offset = 0;
     for (let i = 0; i < levels; i++) {
         const currLevel = levels - 1 - i
-        levelTextures[currLevel].attachFramebuffer(framebuffer, 0)
+        const tf = levelTexturesFramebuffers[currLevel]
+        tf.framebuffer.bind()
+        // levelTextures[currLevel].attachFramebuffer(framebuffer, 0)
 
         const size = Math.pow(2, currLevel)
         // console.log('size', size, 'draw-level', currLevel, 'read-level', levels - i)
@@ -116,9 +139,10 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture)
 
         ValueCell.update(renderable.values.uSize, Math.pow(2, i + 1) / maxSize)
         if (i > 0) {
-            ValueCell.update(renderable.values.tPreviousLevel, levelTextures[levels - i])
+            ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture)
             renderable.update()
         }
+        ctx.state.currentRenderItemId = -1
         renderable.render()
 
         pyramidTexture.bind(0)
@@ -140,15 +164,18 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture)
         offset += size;
     }
 
+    gl.finish()
+
     // printTexture(ctx, pyramidTexture, 2)
 
     //
 
-    const finalCount = getHistopyramidSum(ctx, levelTextures[0])
+    const finalCount = getHistopyramidSum(ctx, levelTexturesFramebuffers[0].texture)
     const height = Math.ceil(finalCount / Math.pow(2, levels))
     const scale = Vec2.create(maxSize / inputTexture.width, maxSize / inputTexture.height)
     // console.log('height', height, 'finalCount', finalCount, 'scale', scale)
 
+    
     return {
         pyramidTex: pyramidTexture,
         count: finalCount,
diff --git a/src/mol-gl/compute/histogram-pyramid/sum.ts b/src/mol-gl/compute/histogram-pyramid/sum.ts
index 2880d4f8939e78650f9b65a2dd89fe09c84c31d2..cc360e859463268776a95bde4d95e128f5024099 100644
--- a/src/mol-gl/compute/histogram-pyramid/sum.ts
+++ b/src/mol-gl/compute/histogram-pyramid/sum.ts
@@ -54,17 +54,33 @@ function getSumTexture(ctx: WebGLContext) {
 /** name for shared framebuffer used for histogram-pyramid operations */
 const FramebufferName = 'histogram-pyramid-sum'
 
+function setRenderingDefaults(ctx: WebGLContext) {
+    const { gl, state } = ctx
+    state.disable(gl.CULL_FACE)
+    state.disable(gl.BLEND)
+    state.disable(gl.DEPTH_TEST)
+    state.disable(gl.SCISSOR_TEST)
+    state.depthMask(false)
+    state.colorMask(true, true, true, true)
+    state.clearColor(0, 0, 0, 0)
+}
+
 const sumArray = new Uint8Array(4)
 export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
     const { gl, framebufferCache } = ctx
 
     const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture)
+    ctx.state.currentRenderItemId = -1
+    
     const framebuffer = framebufferCache.get(FramebufferName).value
     const sumTexture = getSumTexture(ctx)
     sumTexture.attachFramebuffer(framebuffer, 0)
 
+    setRenderingDefaults(ctx)
+
     gl.viewport(0, 0, 1, 1)
     renderable.render()
+    gl.finish()
     ctx.readPixels(0, 0, 1, 1, sumArray)
     ctx.unbindFramebuffer()
 
diff --git a/src/mol-gl/compute/marching-cubes/active-voxels.ts b/src/mol-gl/compute/marching-cubes/active-voxels.ts
index cf1367a8b867a03c1ea64eb318afc9fa16db9fcb..a3d5ab5ab14286b798894e78811d527a9ae6d3ac 100644
--- a/src/mol-gl/compute/marching-cubes/active-voxels.ts
+++ b/src/mol-gl/compute/marching-cubes/active-voxels.ts
@@ -56,7 +56,10 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.disable(gl.CULL_FACE)
     state.disable(gl.BLEND)
     state.disable(gl.DEPTH_TEST)
+    state.disable(gl.SCISSOR_TEST)
     state.depthMask(false)
+    state.colorMask(true, true, true, true)
+    state.clearColor(0, 0, 0, 0)
 }
 
 export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDimensions: Vec3, isoValue: number) {
@@ -70,6 +73,7 @@ export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDime
     activeVoxelsTex.define(width, height)
 
     const renderable = getActiveVoxelsRenderable(ctx, cornerTex, gridDimensions, isoValue)
+    ctx.state.currentRenderItemId = -1
 
     activeVoxelsTex.attachFramebuffer(framebuffer, 0)
     setRenderingDefaults(ctx)
@@ -78,5 +82,7 @@ export function calcActiveVoxels(ctx: WebGLContext, cornerTex: Texture, gridDime
 
     // console.log('at', readTexture(ctx, activeVoxelsTex))
 
+    gl.finish()
+
     return activeVoxelsTex
 }
\ No newline at end of file
diff --git a/src/mol-gl/compute/marching-cubes/isosurface.ts b/src/mol-gl/compute/marching-cubes/isosurface.ts
index 24a4cb508a6c7dc073d1cb0a526d7b9b66652383..1cb19527512dfe93b30352c18343beb7a0e2ce25 100644
--- a/src/mol-gl/compute/marching-cubes/isosurface.ts
+++ b/src/mol-gl/compute/marching-cubes/isosurface.ts
@@ -77,8 +77,10 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.disable(gl.CULL_FACE)
     state.disable(gl.BLEND)
     state.disable(gl.DEPTH_TEST)
-    state.depthMask(false)
     state.enable(gl.SCISSOR_TEST)
+    state.depthMask(false)
+    state.colorMask(true, true, true, true)
+    state.clearColor(0, 0, 0, 0)
 }
 
 export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDimensions: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) {
@@ -87,11 +89,25 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
 
     const framebuffer = framebufferCache.get(FramebufferName).value
 
-    if (!vertexGroupTexture) vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
-    vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
+    let needsClear = false
+
+    if (!vertexGroupTexture) {
+        vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
+        vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
+    } else if (vertexGroupTexture.width !== pyramidTex.width || vertexGroupTexture.height !== pyramidTex.height) {
+        vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
+    } else {
+        needsClear = true
+    }
 
-    if (!normalTexture) normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
-    normalTexture.define(pyramidTex.width, pyramidTex.height)
+    if (!normalTexture) {
+        normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
+        normalTexture.define(pyramidTex.width, pyramidTex.height)
+    } else if (normalTexture.width !== pyramidTex.width || normalTexture.height !== pyramidTex.height) {
+        normalTexture.define(pyramidTex.width, pyramidTex.height)
+    } else {
+        needsClear = true
+    }
 
     // const infoTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
     // infoTex.define(pyramidTex.width, pyramidTex.height)
@@ -109,6 +125,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     // indexTex.define(pyramidTex.width, pyramidTex.height)
 
     const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDimensions, transform, isoValue, levels, scale, count)
+    ctx.state.currentRenderItemId = -1
 
     vertexGroupTexture.attachFramebuffer(framebuffer, 0)
     normalTexture.attachFramebuffer(framebuffer, 1)
@@ -133,9 +150,12 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     setRenderingDefaults(ctx)
     gl.viewport(0, 0, pyramidTex.width, pyramidTex.height)
     gl.scissor(0, 0, pyramidTex.width, height)
+    if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT)
     renderable.render()
     state.disable(gl.SCISSOR_TEST)
 
+    gl.finish()
+
     // const vgt = readTexture(ctx, vertexGroupTexture, pyramidTex.width, height)
     // console.log('vertexGroupTexture', vgt.array.subarray(0, 4 * count))
 
diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts
index 5813109172599f1f08c791d5f7348a152ab14021..bf14f95b6d6fa1b5f9c7aefa4e9463f78f9b4719 100644
--- a/src/mol-gl/renderer.ts
+++ b/src/mol-gl/renderer.ts
@@ -64,6 +64,7 @@ namespace Renderer {
         const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
 
         const viewport = Viewport()
+        const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
 
         const view = Mat4.clone(camera.view)
         const invView = Mat4.invert(Mat4.identity(), view)
@@ -98,23 +99,14 @@ namespace Renderer {
             uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)),
             uFogNear: ValueCell.create(camera.state.fogNear),
             uFogFar: ValueCell.create(camera.state.fogFar),
-            uFogColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.backgroundColor)),
+            uFogColor: ValueCell.create(bgColor),
 
             uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
         }
         const globalUniformList = Object.entries(globalUniforms)
 
-        const [ bgRed, bgGreen, bgBlue ] = Color.toRgbNormalized(p.backgroundColor)
-        gl.clearColor(bgRed, bgGreen, bgBlue, 1.0)
-
-        if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
-            p.backgroundColor = props.backgroundColor
-            const [ r, g, b ] = Color.toRgbNormalized(p.backgroundColor)
-            gl.clearColor(r, g, b, 1.0)
-            ValueCell.update(globalUniforms.uFogColor, Vec3.set(globalUniforms.uFogColor.ref.value, r, g, b))
-        }
-
         let globalUniformsNeedUpdate = true
+
         const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => {
             const program = r.getProgram(variant)
             if (r.state.visible) {
@@ -175,13 +167,19 @@ namespace Renderer {
             ValueCell.update(globalUniforms.uFogNear, camera.state.fogNear)
 
             globalUniformsNeedUpdate = true
+            state.currentRenderItemId = -1
 
             const { renderables } = scene
 
+            state.disable(gl.SCISSOR_TEST)
+            state.disable(gl.BLEND)
+            state.depthMask(true)
+            state.colorMask(true, true, true, true)
+            state.enable(gl.DEPTH_TEST)
+            state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
+            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+
             if (variant === 'draw') {
-                state.disable(gl.BLEND)
-                state.enable(gl.DEPTH_TEST)
-                state.depthMask(true)
                 for (let i = 0, il = renderables.length; i < il; ++i) {
                     const r = renderables[i]
                     if (r.state.opaque) renderObject(r, variant)
@@ -196,9 +194,6 @@ namespace Renderer {
                 }
             } else {
                 // picking
-                state.disable(gl.BLEND)
-                state.enable(gl.DEPTH_TEST)
-                state.depthMask(true)
                 for (let i = 0, il = renderables.length; i < il; ++i) {
                     renderObject(renderables[i], variant)
                 }
@@ -210,6 +205,7 @@ namespace Renderer {
         return {
             clear: () => {
                 state.depthMask(true)
+                state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
                 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
             },
             render,
@@ -221,9 +217,8 @@ namespace Renderer {
                 }
                 if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
                     p.backgroundColor = props.backgroundColor
-                    const [ r, g, b ] = Color.toRgbNormalized(p.backgroundColor)
-                    gl.clearColor(r, g, b, 1.0)
-                    ValueCell.update(globalUniforms.uFogColor, Vec3.set(globalUniforms.uFogColor.ref.value, r, g, b))
+                    Color.toVec3Normalized(bgColor, p.backgroundColor)
+                    ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
                 }
                 if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
                     p.lightIntensity = props.lightIntensity
diff --git a/src/mol-gl/shader/gaussian-density.frag b/src/mol-gl/shader/gaussian-density.frag
index a78f893d0925baa6a2b91c122cdc2dd65c8fbc9c..2e7c82d3ae7deda046c7ff072e04d423c8de8a64 100644
--- a/src/mol-gl/shader/gaussian-density.frag
+++ b/src/mol-gl/shader/gaussian-density.frag
@@ -28,8 +28,6 @@ varying float vRadius;
 #pragma glslify: texture3dFrom2dNearest = require(./utils/texture3d-from-2d-nearest.glsl, intMod=intMod, intDiv=intDiv, foo=foo) // foo=foo is a workaround for a bug in glslify
 
 uniform vec3 uBboxSize;
-uniform vec3 uBboxMin;
-uniform vec3 uBboxMax;
 uniform vec3 uGridDim;
 uniform float uCurrentSlice;
 uniform float uCurrentX;
@@ -56,7 +54,7 @@ void main() {
     #if defined(dCalcType_density)
         float radiusSq = vRadius * vRadius;
         float density = exp(-uAlpha * ((dist * dist) / radiusSq));
-        gl_FragColor = vec4(density);
+        gl_FragColor.a = density;
     #elif defined(dCalcType_minDistance)
         gl_FragColor.a = 10000.0 - dist;
         // gl_FragColor.a = 1.0 - encodeFloatLog(dist);
diff --git a/src/mol-gl/shader/gaussian-density.vert b/src/mol-gl/shader/gaussian-density.vert
index f7557182942ce69b7861146dd3da32ffc3b5c52d..6db1ff7c8dc08e800402351964eeeb32210e2b41 100644
--- a/src/mol-gl/shader/gaussian-density.vert
+++ b/src/mol-gl/shader/gaussian-density.vert
@@ -20,7 +20,6 @@ varying float vRadius;
 
 uniform vec3 uBboxSize;
 uniform vec3 uBboxMin;
-uniform vec3 uBboxMax;
 uniform vec3 uGridDim;
 uniform float uCurrentSlice;
 
@@ -29,7 +28,7 @@ void main() {
     #if defined(dCalcType_groupId)
         vGroup = aGroup;
     #endif
-    float scale = max(uBboxSize.z, max(uBboxSize.x, uBboxSize.y));
+    float scale = max(uBboxSize.x, uBboxSize.y);
     gl_PointSize = (vRadius / scale) * max(uGridDim.x, uGridDim.y) * 6.0;
     vPosition = (aPosition - uBboxMin) / uBboxSize;
     gl_Position = vec4(vPosition * 2.0 - 1.0, 1.0);
diff --git a/src/mol-gl/webgl/context.ts b/src/mol-gl/webgl/context.ts
index 09f567e2551d59c8e3f013f2f75de38680173928..52e957e04d8cbbafcd1f3d32e77aa6f7c8d0d7bb 100644
--- a/src/mol-gl/webgl/context.ts
+++ b/src/mol-gl/webgl/context.ts
@@ -245,6 +245,7 @@ function createStats(): WebGLStats {
 export type WebGLState = {
     currentProgramId: number
     currentMaterialId: number
+    currentRenderItemId: number
 
     enable: (cap: number) => void
     disable: (cap: number) => void
@@ -252,6 +253,8 @@ export type WebGLState = {
     frontFace: (mode: number) => void
     cullFace: (mode: number) => void
     depthMask: (flag: boolean) => void
+    colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => void
+    clearColor: (red: number, green: number, blue: number, alpha: number) => void
 
     blendFunc: (src: number, dst: number) => void
     blendFuncSeparate: (srcRGB: number, dstRGB: number, srcAlpha: number, dstAlpha: number) => void
@@ -266,6 +269,8 @@ function createState(gl: GLRenderingContext): WebGLState {
     let currentFrontFace = gl.getParameter(gl.FRONT_FACE)
     let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE)
     let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK)
+    let currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK)
+    let currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE)
 
     let currentBlendSrcRGB = gl.getParameter(gl.BLEND_SRC_RGB)
     let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB)
@@ -278,6 +283,7 @@ function createState(gl: GLRenderingContext): WebGLState {
     return {
         currentProgramId: -1,
         currentMaterialId: -1,
+        currentRenderItemId: -1,
 
         enable: (cap: number) => {
             if (enabledCapabilities[cap] !== true ) {
@@ -310,6 +316,22 @@ function createState(gl: GLRenderingContext): WebGLState {
                 currentDepthMask = flag
             }
         },
+        colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => {
+            if (red !== currentColorMask[0] || green !== currentColorMask[1] || blue !== currentColorMask[2] || alpha !== currentColorMask[3])
+            gl.colorMask(red, green, blue, alpha)
+            currentColorMask[0] = red
+            currentColorMask[1] = green
+            currentColorMask[2] = blue
+            currentColorMask[3] = alpha
+        },
+        clearColor: (red: number, green: number, blue: number, alpha: number) => {
+            if (red !== currentClearColor[0] || green !== currentClearColor[1] || blue !== currentClearColor[2] || alpha !== currentClearColor[3])
+            gl.clearColor(red, green, blue, alpha)
+            currentClearColor[0] = red
+            currentClearColor[1] = green
+            currentClearColor[2] = blue
+            currentClearColor[3] = alpha
+        },
 
         blendFunc: (src: number, dst: number) => {
             if (src !== currentBlendSrcRGB || dst !== currentBlendDstRGB || src !== currentBlendSrcAlpha || dst !== currentBlendDstAlpha) {
diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts
index e196b6ee08935a2975a435a47cb4bdce311a48bd..b516fb8099172ed50c21646934ab016174f4a947 100644
--- a/src/mol-gl/webgl/render-item.ts
+++ b/src/mol-gl/webgl/render-item.ts
@@ -96,8 +96,8 @@ export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode,
 }
 
 export type ComputeRenderItem = RenderItem<keyof typeof ComputeRenderVariantDefines & string>
-export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues) {
-    return createRenderItem(ctx, drawMode, shaderCode, schema, values, -1, ComputeRenderVariantDefines)
+export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId = -1) {
+    return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, ComputeRenderVariantDefines)
 }
 
 /**
@@ -164,25 +164,31 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
         render: (variant: S) => {
             if (drawCount === 0 || instanceCount === 0) return
             const program = programs[variant].value
-            const vertexArray = vertexArrays[variant]
-            if (program.id !== state.currentProgramId || program.id !== currentProgramId ||
-                materialId === -1 || materialId !== state.currentMaterialId
-            ) {
-                // console.log('program.id changed or materialId changed/-1', materialId)
-                if (program.id !== state.currentProgramId) program.use()
-                program.setUniforms(materialUniformValueEntries)
-                state.currentMaterialId = materialId
-                currentProgramId = program.id
-            }
-            program.setUniforms(uniformValueEntries)
-            program.bindTextures(textures)
-            if (vertexArrayObject && vertexArray) {
-                vertexArrayObject.bindVertexArray(vertexArray)
-                // need to bind elements buffer explicitly since it is not always recorded in the VAO
-                if (elementsBuffer) elementsBuffer.bind()
+            if (program.id === currentProgramId && state.currentRenderItemId === id) {
+                program.setUniforms(uniformValueEntries)
+                program.bindTextures(textures)
             } else {
-                if (elementsBuffer) elementsBuffer.bind()
-                program.bindAttributes(attributeBuffers)
+                const vertexArray = vertexArrays[variant]
+                if (program.id !== state.currentProgramId || program.id !== currentProgramId ||
+                    materialId === -1 || materialId !== state.currentMaterialId
+                ) {
+                    // console.log('program.id changed or materialId changed/-1', materialId)
+                    if (program.id !== state.currentProgramId) program.use()
+                    program.setUniforms(materialUniformValueEntries)
+                    state.currentMaterialId = materialId
+                    currentProgramId = program.id
+                }
+                program.setUniforms(uniformValueEntries)
+                program.bindTextures(textures)
+                if (vertexArrayObject && vertexArray) {
+                    vertexArrayObject.bindVertexArray(vertexArray)
+                    // need to bind elements buffer explicitly since it is not always recorded in the VAO
+                    if (elementsBuffer) elementsBuffer.bind()
+                } else {
+                    if (elementsBuffer) elementsBuffer.bind()
+                    program.bindAttributes(attributeBuffers)
+                }
+                state.currentRenderItemId = id
             }
             if (isDebugMode) {
                 checkFramebufferStatus(ctx.gl)
diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts
index 8036d65c3ecf2df76bd4837711e96a30ad360554..448f6dfa2938776873400bf14f7c5c688a92e1b6 100644
--- a/src/mol-math/geometry/gaussian-density/gpu.ts
+++ b/src/mol-math/geometry/gaussian-density/gpu.ts
@@ -30,12 +30,11 @@ export const GaussianDensitySchema = {
     uCurrentSlice: UniformSpec('f'),
     uCurrentX: UniformSpec('f'),
     uCurrentY: UniformSpec('f'),
-    uBboxMin: UniformSpec('v3'),
-    uBboxMax: UniformSpec('v3'),
-    uBboxSize: UniformSpec('v3'),
-    uGridDim: UniformSpec('v3'),
-    uGridTexDim: UniformSpec('v3'),
-    uAlpha: UniformSpec('f'),
+    uBboxMin: UniformSpec('v3', true),
+    uBboxSize: UniformSpec('v3', true),
+    uGridDim: UniformSpec('v3', true),
+    uGridTexDim: UniformSpec('v3', true),
+    uAlpha: UniformSpec('f', true),
     tMinDistanceTex: TextureSpec('texture', 'rgba', 'float', 'nearest'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),
@@ -106,7 +105,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
 
     //
 
-    const { gl, framebufferCache } = webgl
+    const { gl, framebufferCache, state } = webgl
     const { uCurrentSlice, uCurrentX, uCurrentY } = renderable.values
 
     const framebuffer = framebufferCache.get(FramebufferName).value
@@ -116,8 +115,12 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
     if (!texture) texture = createTexture(webgl, 'image-float32', 'rgba', 'float', 'nearest')
     texture.define(texDimX, texDimY)
 
-    function render(fbTex: Texture) {
+    // console.log(renderable)
+
+    function render(fbTex: Texture, clear: boolean) {
+        state.currentRenderItemId = -1
         fbTex.attachFramebuffer(framebuffer, 0)
+        if (clear) gl.clear(gl.COLOR_BUFFER_BIT)
         let currCol = 0
         let currY = 0
         let currX = 0
@@ -126,25 +129,31 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
                 currCol -= texCols
                 currY += dy
                 currX = 0
+                ValueCell.update(uCurrentY, currY)
             }
-            gl.viewport(currX, currY, dx, dy)
-            ValueCell.update(uCurrentSlice, i)
+            // console.log({ i, currX, currY })
             ValueCell.update(uCurrentX, currX)
-            ValueCell.update(uCurrentY, currY)
+            ValueCell.update(uCurrentSlice, i)
+            gl.viewport(currX, currY, dx, dy)
             renderable.render()
             ++currCol
             currX += dx
         }
     }
 
-    setupMinDistanceRendering(webgl, renderable)
-    render(minDistanceTexture)
-
     setupDensityRendering(webgl, renderable)
-    render(texture)
+    render(texture, true)
+
+    setupMinDistanceRendering(webgl, renderable)
+    render(minDistanceTexture, true)
+    gl.finish()
 
     setupGroupIdRendering(webgl, renderable)
-    render(texture)
+    render(texture, false)
+
+    // printTexture(webgl, texture, 1)
+
+    gl.finish()
 
     return { texture, scale: Vec3.inverse(Vec3.zero(), delta), bbox: expandedBox, dim }
 }
@@ -246,7 +255,6 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po
         uCurrentX: ValueCell.create(0),
         uCurrentY: ValueCell.create(0),
         uBboxMin: ValueCell.create(box.min),
-        uBboxMax: ValueCell.create(box.max),
         uBboxSize: ValueCell.create(extent),
         uGridDim: ValueCell.create(dimensions),
         uGridTexDim: ValueCell.create(Vec3.create(texDimX, texDimY, 0)),
@@ -267,15 +275,18 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po
 function setRenderingDefaults(ctx: WebGLContext) {
     const { gl, state } = ctx
     state.disable(gl.CULL_FACE)
-    state.frontFace(gl.CCW)
-    state.cullFace(gl.BACK)
     state.enable(gl.BLEND)
+    state.disable(gl.DEPTH_TEST)
+    state.disable(gl.SCISSOR_TEST)
+    state.depthMask(false)
+    state.clearColor(0, 0, 0, 0)
 }
 
 function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRenderable<any>) {
     const { gl, state } = webgl
     ValueCell.update(renderable.values.dCalcType, 'minDistance')
     renderable.update()
+    state.colorMask(false, false, false, true)
     state.blendFunc(gl.ONE, gl.ONE)
     // the shader writes 1 - dist so we set blending to MAX
     state.blendEquation(webgl.extensions.blendMinMax.MAX)
@@ -285,6 +296,7 @@ function setupDensityRendering(webgl: WebGLContext, renderable: ComputeRenderabl
     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.blendEquation(gl.FUNC_ADD)
 }
@@ -294,7 +306,8 @@ function setupGroupIdRendering(webgl: WebGLContext, renderable: ComputeRenderabl
     ValueCell.update(renderable.values.dCalcType, 'groupId')
     renderable.update()
     // overwrite color, don't change alpha
-    state.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ZERO, gl.ONE)
+    state.colorMask(true, true, true, false)
+    state.blendFunc(gl.ONE, gl.ZERO)
     state.blendEquation(gl.FUNC_ADD)
 }
 
diff --git a/src/tests/browser/marching-cubes.ts b/src/tests/browser/marching-cubes.ts
index a0ee2f5389f74bdaddb8dbaa8dbf44883c57ba8c..783fd9dc474b17cb0dc7b9c7f7d0d632a63d921e 100644
--- a/src/tests/browser/marching-cubes.ts
+++ b/src/tests/browser/marching-cubes.ts
@@ -56,7 +56,7 @@ async function init() {
     }
     const isoValue = Math.exp(-props.smoothness)
 
-    if (false) {
+    if (true) {
         console.time('gpu gaussian2')
         const densityTextureData2 = await computeGaussianDensityTexture2d(position, box, radius, props, webgl).run()
         webgl.waitForGpuCommandsCompleteSync()
@@ -119,7 +119,7 @@ async function init() {
     //
 
     console.time('cpu gaussian')
-    const densityData = await computeGaussianDensity(position, box, radius, { ...props, useGpu: false }, webgl).run()
+    const densityData = await computeGaussianDensity(position, box, radius, { ...props, useGpu: true }, webgl).run()
     console.timeEnd('cpu gaussian')
     // console.log({ densityData })
 
@@ -134,7 +134,6 @@ async function init() {
     console.timeEnd('cpu mc')
     // console.log('surface', surface)
     Mesh.transformImmediate(surface, densityData.transform)
-    Mesh.computeNormalsImmediate(surface)
     const meshProps = { doubleSided: true, flatShaded: false, alpha: 1.0 }
     const meshValues = Mesh.Utils.createValuesSimple(surface, meshProps, Color(0x995511), 1)
     const meshState = Mesh.Utils.createRenderableState(meshProps)