diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c282c0cb71ab886338287d29e03b0d489cc5b14..a2f0adaddb84c5e054610ed29f2873f8f8648744 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Optimize BinaryCIF integer packing encoder
 - Fix Dual depth peeling when post-processing is off or when rendering direct-volumes
 - Add `cameraClipping.minNear` parameter
 
diff --git a/src/mol-io/common/binary-cif/array-encoder.ts b/src/mol-io/common/binary-cif/array-encoder.ts
index 08dfee8f6d315a0c995cdc0939f7d76648714365..7ca14219f7777ca448e5b5ae04615e7f07205023 100644
--- a/src/mol-io/common/binary-cif/array-encoder.ts
+++ b/src/mol-io/common/binary-cif/array-encoder.ts
@@ -264,28 +264,35 @@ export namespace ArrayEncoding {
         return false;
     }
 
-    function packingSize(data: Int32Array, upperLimit: number) {
+    function packingSizeUnsigned(data: Int32Array, upperLimit: number) {
+        let size = 0;
+        for (let i = 0, n = data.length; i < n; i++) {
+            size += (data[i] / upperLimit) | 0;
+        }
+        size += data.length;
+        return size;
+    }
+
+
+    function packingSizeSigned(data: Int32Array, upperLimit: number) {
         const lowerLimit = -upperLimit - 1;
         let size = 0;
         for (let i = 0, n = data.length; i < n; i++) {
             const value = data[i];
-            if (value === 0) {
-                size += 1;
-            } else if (value > 0) {
-                size += Math.ceil(value / upperLimit);
-                if (value % upperLimit === 0) size += 1;
+            if (value >= 0) {
+                size += (value / upperLimit) | 0;
             } else {
-                size += Math.ceil(value / lowerLimit);
-                if (value % lowerLimit === 0) size += 1;
+                size += (value / lowerLimit) | 0;
             }
         }
+        size += data.length;
         return size;
     }
 
     function determinePacking(data: Int32Array): { isSigned: boolean, size: number, bytesPerElement: number } {
         const signed = isSigned(data);
-        const size8 = signed ? packingSize(data, 0x7F) : packingSize(data, 0xFF);
-        const size16 = signed ? packingSize(data, 0x7FFF) : packingSize(data, 0xFFFF);
+        const size8 = signed ? packingSizeSigned(data, 0x7F) : packingSizeUnsigned(data, 0xFF);
+        const size16 = signed ? packingSizeSigned(data, 0x7FFF) : packingSizeUnsigned(data, 0xFFFF);
 
         if (data.length * 4 < size16 * 2) {
             // 4 byte packing is the most effective