diff --git a/CHANGELOG.md b/CHANGELOG.md
index b880389397ad2b8dea2402c3b4a1670d05f69f3c..d501c71631412a80938a1c598c0dcd02cbc68efe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Avoid `renderMarkingDepth` for fully transparent renderables
 - Remove `camera.far` doubling workaround
 - Add `ModifiersKeys.areNone` helper function
+- Fix rendering issues caused by VAO reuse
 - Add "Zoom All", "Orient Axes", "Reset Axes" buttons to the "Reset Camera" button
 - Improve trackball move-state handling when key bindings use modifiers
 
diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts
index 40215246c7f01c866d84de66303e1b51db7c4cb9..4842ca9c9407e97059239e3b167f945c3acbbd6f 100644
--- a/src/mol-gl/_spec/renderer.spec.ts
+++ b/src/mol-gl/_spec/renderer.spec.ts
@@ -53,7 +53,7 @@ describe('renderer', () => {
         scene.commit();
         expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
         expect(ctx.stats.resourceCounts.texture).toBe(9);
-        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 1 : 0);
+        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 6 : 0);
         expect(ctx.stats.resourceCounts.program).toBe(6);
         expect(ctx.stats.resourceCounts.shader).toBe(12);
 
diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts
index 03f99fb5f0e6c9311f49f024468fbb2f17f4dda5..252da58f816b85b579d10981bc867a674230d7c2 100644
--- a/src/mol-gl/webgl/render-item.ts
+++ b/src/mol-gl/webgl/render-item.ts
@@ -144,7 +144,10 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
         elementsBuffer = resources.elements(elements.ref.value);
     }
 
-    let vertexArray: VertexArray | null = vertexArrayObject ? resources.vertexArray(programs, attributeBuffers, elementsBuffer) : null;
+    const vertexArrays: Record<string, VertexArray | null> = {};
+    for (const k of renderVariants) {
+        vertexArrays[k] = vertexArrayObject ? resources.vertexArray(programs[k], attributeBuffers, elementsBuffer) : null;
+    }
 
     let drawCount: number = values.drawCount.ref.value;
     let instanceCount: number = values.instanceCount.ref.value;
@@ -170,6 +173,7 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
                 program.setUniforms(uniformValueEntries);
                 program.bindTextures(textures, sharedTexturesCount);
             } else {
+                const vertexArray = vertexArrays[variant];
                 if (program.id !== state.currentProgramId || program.id !== currentProgramId ||
                     materialId === -1 || materialId !== state.currentMaterialId
                 ) {
@@ -291,9 +295,12 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
             }
 
             if (valueChanges.attributes || valueChanges.defines || valueChanges.elements) {
-                // console.log('program/defines or buffers changed, update vao');
-                if (vertexArray) vertexArray.destroy();
-                vertexArray = vertexArrayObject ? resources.vertexArray(programs, attributeBuffers, elementsBuffer) : null;
+                // console.log('program/defines or buffers changed, update vaos');
+                for (const k of renderVariants) {
+                    const vertexArray = vertexArrays[k];
+                    if (vertexArray) vertexArray.destroy();
+                    vertexArrays[k] = vertexArrayObject ? resources.vertexArray(programs[k], attributeBuffers, elementsBuffer) : null;
+                }
             }
 
             for (let i = 0, il = textures.length; i < il; ++i) {
@@ -341,8 +348,9 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode:
             if (!destroyed) {
                 for (const k of renderVariants) {
                     programs[k].destroy();
+                    const vertexArray = vertexArrays[k];
+                    if (vertexArray) vertexArray.destroy();
                 }
-                if (vertexArray) vertexArray.destroy();
                 textures.forEach(([k, texture]) => {
                     // lifetime of textures with kind 'texture' is defined externally
                     if (schema[k].kind !== 'texture') texture.destroy();
diff --git a/src/mol-gl/webgl/resources.ts b/src/mol-gl/webgl/resources.ts
index ae036cd3812072870c6d32ed930dc201b9699904..dc75c5d60257aec9511c97926f6b94d6f7dd54fc 100644
--- a/src/mol-gl/webgl/resources.ts
+++ b/src/mol-gl/webgl/resources.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { ProgramProps, createProgram, Program, Programs } from './program';
+import { ProgramProps, createProgram, Program } from './program';
 import { ShaderType, createShader, Shader, ShaderProps } from './shader';
 import { GLRenderingContext } from './compat';
 import { Framebuffer, createFramebuffer } from './framebuffer';
@@ -60,7 +60,7 @@ export interface WebGLResources {
     shader: (type: ShaderType, source: string) => Shader
     texture: (kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter) => Texture,
     cubeTexture: (faces: CubeFaces, mipmaps: boolean, onload?: () => void) => Texture,
-    vertexArray: (programs: Programs, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => VertexArray,
+    vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => VertexArray,
 
     getByteCounts: () => ByteCounts
 
@@ -142,8 +142,8 @@ export function createResources(gl: GLRenderingContext, state: WebGLState, stats
         cubeTexture: (faces: CubeFaces, mipmaps: boolean, onload?: () => void) => {
             return wrap('cubeTexture', createCubeTexture(gl, faces, mipmaps, onload));
         },
-        vertexArray: (programs: Programs, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
-            return wrap('vertexArray', createVertexArray(gl, extensions, programs, attributeBuffers, elementsBuffer));
+        vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
+            return wrap('vertexArray', createVertexArray(gl, extensions, program, attributeBuffers, elementsBuffer));
         },
 
         getByteCounts: () => {
diff --git a/src/mol-gl/webgl/vertex-array.ts b/src/mol-gl/webgl/vertex-array.ts
index 966bb4d92ecff088d90cb95ff040d1f92fd67599..1e6eec37943142870432f49bfc734ca0e5f5d3a8 100644
--- a/src/mol-gl/webgl/vertex-array.ts
+++ b/src/mol-gl/webgl/vertex-array.ts
@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Programs } from './program';
+import { Program } from './program';
 import { ElementsBuffer, AttributeBuffers } from './buffer';
 import { WebGLExtensions } from './extensions';
 import { idFactory } from '../../mol-util/id-factory';
@@ -41,7 +41,7 @@ export interface VertexArray {
     destroy: () => void
 }
 
-export function createVertexArray(gl: GLRenderingContext, extensions: WebGLExtensions, programs: Programs, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer): VertexArray {
+export function createVertexArray(gl: GLRenderingContext, extensions: WebGLExtensions, program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer): VertexArray {
     const id = getNextVertexArrayId();
     let vertexArray = getVertexArray(extensions);
     let vertexArrayObject = getVertexArrayObject(extensions);
@@ -49,7 +49,7 @@ export function createVertexArray(gl: GLRenderingContext, extensions: WebGLExten
     function update() {
         vertexArrayObject.bindVertexArray(vertexArray);
         if (elementsBuffer) elementsBuffer.bind();
-        for (const p of Object.values(programs)) p.bindAttributes(attributeBuffers);
+        program.bindAttributes(attributeBuffers);
         vertexArrayObject.bindVertexArray(null);
     }