diff --git a/src/mol-geo/geometry/transform-data.ts b/src/mol-geo/geometry/transform-data.ts
index 486400aa21a6a1f6f62e820df05d7855c87b667a..03e107a88ec684ae447cd1ec06d18a6d57ba26ef 100644
--- a/src/mol-geo/geometry/transform-data.ts
+++ b/src/mol-geo/geometry/transform-data.ts
@@ -42,15 +42,12 @@ export function createIdentityTransform(transformData?: TransformData): Transfor
     return createTransform(new Float32Array(identityTransform), 1, transformData)
 }
 
-const tmpTransformMat4 = Mat4.identity()
 export function setTransformData(matrix: Mat4, transformData: TransformData) {
     const instanceCount = transformData.instanceCount.ref.value
     const transform = transformData.transform.ref.value
     const aTransform = transformData.aTransform.ref.value
     for (let i = 0; i < instanceCount; i++) {
-        Mat4.fromArray(tmpTransformMat4, transform, i * 16)
-        Mat4.mul(tmpTransformMat4, tmpTransformMat4, matrix)
-        Mat4.toArray(tmpTransformMat4, aTransform, i * 16)
+        Mat4.mulOffset(aTransform, transform, matrix, i * 16, i * 16, 0)
     }
     ValueCell.update(transformData.aTransform, aTransform)
 }
\ No newline at end of file
diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts
index c1bbdb345a195bbbdbc6eb62ffeaa6e109bce385..02e7cf29fc268e371204a36eba42a77472164a16 100644
--- a/src/mol-gl/renderable/util.ts
+++ b/src/mol-gl/renderable/util.ts
@@ -5,7 +5,7 @@
  */
 
 import { Sphere3D } from 'mol-math/geometry'
-import { Mat4, Vec3 } from 'mol-math/linear-algebra'
+import { Vec3 } from 'mol-math/linear-algebra'
 import { BoundaryHelper } from 'mol-math/geometry/boundary-helper';
 
 export function calculateTextureInfo (n: number, itemSize: number) {
@@ -36,7 +36,6 @@ export function createTextureImage(n: number, itemSize: number): TextureImage<Ui
 
 //
 
-const m = Mat4.zero()
 const v = Vec3.zero()
 const boundaryHelper = new BoundaryHelper()
 
@@ -58,12 +57,12 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
     const { center, radius } = invariantBoundingSphere
     boundaryHelper.reset(0)
     for (let i = 0, _i = transformCount; i < _i; ++i) {
-        Vec3.transformMat4(v, center, Mat4.fromArray(m, transform, i * 16))
+        Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
         boundaryHelper.boundaryStep(v, radius)
     }
     boundaryHelper.finishBoundaryStep()
     for (let i = 0, _i = transformCount; i < _i; ++i) {
-        Vec3.transformMat4(v, center, Mat4.fromArray(m, transform, i * 16))
+        Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
         boundaryHelper.extendStep(v, radius)
     }
     return boundaryHelper.getSphere()
diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts
index 482fc13e90157e145f8fd017cb054d465fa1935f..be275d960bf494d3937f4537d666336ddeed7d7a 100644
--- a/src/mol-math/linear-algebra/3d/mat4.ts
+++ b/src/mol-math/linear-algebra/3d/mat4.ts
@@ -367,6 +367,42 @@ namespace Mat4 {
         return out;
     }
 
+    /**
+     * Like `mul` but with offsets into arrays
+     */
+    export function mulOffset(out: Helpers.NumberArray, a: Helpers.NumberArray, b: Helpers.NumberArray, oOut: number, oA: number, oB: number) {
+        const a00 = a[0 + oA], a01 = a[1 + oA], a02 = a[2 + oA], a03 = a[3 + oA],
+            a10 = a[4 + oA], a11 = a[5 + oA], a12 = a[6 + oA], a13 = a[7 + oA],
+            a20 = a[8 + oA], a21 = a[9 + oA], a22 = a[10 + oA], a23 = a[11 + oA],
+            a30 = a[12 + oA], a31 = a[13 + oA], a32 = a[14 + oA], a33 = a[15 + oA];
+
+        // Cache only the current line of the second matrix
+        let b0 = b[0 + oB], b1 = b[1 + oB], b2 = b[2 + oB], b3 = b[3 + oB];
+        out[0 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+        out[1 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+        out[2 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+        out[3 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+        b0 = b[4 + oB]; b1 = b[5 + oB]; b2 = b[6 + oB]; b3 = b[7 + oB];
+        out[4 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+        out[5 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+        out[6 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+        out[7 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+        b0 = b[8 + oB]; b1 = b[9 + oB]; b2 = b[10 + oB]; b3 = b[11 + oB];
+        out[8 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+        out[9 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+        out[10 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+        out[11 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+
+        b0 = b[12 + oB]; b1 = b[13 + oB]; b2 = b[14 + oB]; b3 = b[15 + oB];
+        out[12 + oOut] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+        out[13 + oOut] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+        out[14 + oOut] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+        out[15 + oOut] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+        return out;
+    }
+
     export function mul3(out: Mat4, a: Mat4, b: Mat4, c: Mat4) {
         return mul(out, mul(out, a, b), c);
     }
diff --git a/src/mol-math/linear-algebra/3d/vec3.ts b/src/mol-math/linear-algebra/3d/vec3.ts
index 55e6f7662eece83613b237245a79e05c3b3dd5fe..5a230ea9054ebe66f723285bd26e79f0897c92e6 100644
--- a/src/mol-math/linear-algebra/3d/vec3.ts
+++ b/src/mol-math/linear-algebra/3d/vec3.ts
@@ -369,6 +369,18 @@ namespace Vec3 {
         return out;
     }
 
+    /**
+     * Like `transformMat4` but with offsets into arrays
+     */
+    export function transformMat4Offset(out: Helpers.NumberArray, a: Helpers.NumberArray, m: Helpers.NumberArray, outO: number, aO: number, oM: number) {
+        const x = a[0 + aO], y = a[1 + aO], z = a[2 + aO],
+            w = 1 / ((m[3 + oM] * x + m[7 + oM] * y + m[11 + oM] * z + m[15 + oM]) || 1.0);
+        out[0 + outO] = (m[0 + oM] * x + m[4 + oM] * y + m[8 + oM] * z + m[12 + oM]) * w;
+        out[1 + outO] = (m[1 + oM] * x + m[5 + oM] * y + m[9 + oM] * z + m[13 + oM]) * w;
+        out[2 + outO] = (m[2 + oM] * x + m[6 + oM] * y + m[10 + oM] * z + m[14 + oM]) * w;
+        return out;
+    }
+
     /**
      * Transforms the Vec3 with a Mat3.
      */
diff --git a/src/mol-repr/visual.ts b/src/mol-repr/visual.ts
index 51805957d731001be2fcdffd9e2e58b224fabec1..79a378b1e37e52edf72a728474f83a9c679d74f6 100644
--- a/src/mol-repr/visual.ts
+++ b/src/mol-repr/visual.ts
@@ -15,7 +15,6 @@ import { Theme } from 'mol-theme/theme';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { setTransformData } from 'mol-geo/geometry/transform-data';
 import { calculateTransformBoundingSphere } from 'mol-gl/renderable/util';
-import { Sphere3D } from 'mol-math/geometry';
 import { ValueCell } from 'mol-util';
 
 export interface VisualContext {
@@ -53,9 +52,7 @@ namespace Visual {
             const { values } = renderObject
             setTransformData(value, values)
             const boundingSphere = calculateTransformBoundingSphere(values.invariantBoundingSphere.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value)
-            if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
-                ValueCell.update(values.boundingSphere, boundingSphere)
-            }
+            ValueCell.update(values.boundingSphere, boundingSphere)
         }
     }
 }
\ No newline at end of file