diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
index aee1b25a9575be96c945a742b2a453965e45fe80..09132638b89c66675a0723bf8b245d808442a50b 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
@@ -31,6 +31,22 @@
 
 using namespace armarx;
 
+struct armarx::ArVizWidgetBatchCallback : IceUtil::Shared
+{
+    class ArVizWidgetController* this_;
+
+    void onSuccess(armarx::viz::RecordingBatch const& batch)
+    {
+        this_->onGetBatchAsync(batch);
+    }
+
+    void onFailure(Ice::Exception const& ex)
+    {
+        ARMARX_WARNING << "Failed to get batch async.\nReason:"
+                       << ex;
+    }
+};
+
 ArVizWidgetController::ArVizWidgetController()
 {
     widget.setupUi(getWidget());
@@ -80,6 +96,13 @@ void ArVizWidgetController::onInitComponent()
         usingProxy(storageName);
     }
     ARMARX_IMPORTANT << "OnInit: " << storageName;
+
+    callbackData = new ArVizWidgetBatchCallback();
+    callbackData->this_ = this;
+    callback = viz::newCallback_StorageInterface_getRecordingBatch(
+                   callbackData,
+                   &ArVizWidgetBatchCallback::onSuccess,
+                   &ArVizWidgetBatchCallback::onFailure);
 }
 
 void armarx::ArVizWidgetController::onExitComponent()
@@ -479,18 +502,6 @@ void ArVizWidgetController::selectRecording(const viz::Recording& recording)
     currentRecording = recording;
     currentRecordingSelected = true;
     enableWidgetAccordingToMode();
-
-    widget.replayRevisionSpinBox->blockSignals(true);
-    widget.replayRevisionSpinBox->setMinimum(recording.firstRevision);
-    widget.replayRevisionSpinBox->setMaximum(recording.lastRevision);
-    widget.replayRevisionSpinBox->setValue(recording.firstRevision);
-    widget.replayRevisionSpinBox->blockSignals(false);
-
-    widget.replayRevisionSlider->blockSignals(true);
-    widget.replayRevisionSlider->setMinimum(recording.firstRevision);
-    widget.replayRevisionSlider->setMaximum(recording.lastRevision);
-    widget.replayRevisionSlider->setValue(recording.firstRevision);
-    widget.replayRevisionSlider->blockSignals(false);
 }
 
 void ArVizWidgetController::onReplayStart(bool)
@@ -504,6 +515,18 @@ void ArVizWidgetController::onReplayStart(bool)
 
     changeMode(ArVizWidgetMode::Replaying);
 
+    widget.replayRevisionSpinBox->blockSignals(true);
+    widget.replayRevisionSpinBox->setMinimum(currentRecording.firstRevision);
+    widget.replayRevisionSpinBox->setMaximum(currentRecording.lastRevision);
+    widget.replayRevisionSpinBox->setValue(currentRecording.firstRevision);
+    widget.replayRevisionSpinBox->blockSignals(false);
+
+    widget.replayRevisionSlider->blockSignals(true);
+    widget.replayRevisionSlider->setMinimum(currentRecording.firstRevision);
+    widget.replayRevisionSlider->setMaximum(currentRecording.lastRevision);
+    widget.replayRevisionSlider->setValue(currentRecording.firstRevision);
+    widget.replayRevisionSlider->blockSignals(false);
+
     onReplaySliderChanged(widget.replayRevisionSlider->value());
 }
 
@@ -541,10 +564,10 @@ long ArVizWidgetController::replayToRevision(long revision)
 
     viz::RecordingBatch const& batch = getRecordingBatch(matchingBatchHeader->index);
 
-    ARMARX_INFO << "Replaying to revision : " << revision
-                << "\nGot batch: " << batch.header.firstRevision << " - " << batch.header.lastRevision
-                << "\nUpdates: " << batch.updates.size()
-                << "\nInitial state: " << batch.initialState.size();
+    ARMARX_VERBOSE << "Replaying to revision : " << revision
+                   << "\nGot batch: " << batch.header.firstRevision << " - " << batch.header.lastRevision
+                   << "\nUpdates: " << batch.updates.size()
+                   << "\nInitial state: " << batch.initialState.size();
 
 
     auto revisionLess = [](viz::TimestampedLayerUpdate const & lhs, viz::TimestampedLayerUpdate const & rhs)
@@ -631,17 +654,49 @@ void ArVizWidgetController::enableWidgetAccordingToMode()
     }
 }
 
+void ArVizWidgetController::onGetBatchAsync(const viz::RecordingBatch& batch)
+{
+    // We received a batch asynchronously ==> Update the cache
+    ARMARX_INFO << "Received async batch: " << batch.header.index;
+    std::unique_lock<std::mutex> lock(recordingBatchCacheMutex);
+
+    auto& entry = recordingBatchCache[batch.header.index];
+    entry.data = batch;
+    entry.lastUsed = IceUtil::Time::now();
+}
+
 viz::RecordingBatch const& ArVizWidgetController::getRecordingBatch(long index)
 {
     IceUtil::Time now = IceUtil::Time::now();
+
+    std::unique_lock<std::mutex> lock(recordingBatchCacheMutex);
+
     auto iter = recordingBatchCache.find(index);
     if (iter != recordingBatchCache.end())
     {
+        // Start prefetching neighbouring batches
+        bool asyncPrefetchIsRunning = callbackResult && !callbackResult->isCompleted();
+        if (!asyncPrefetchIsRunning)
+        {
+            if (index + 1 < (long)currentRecording.batchHeaders.size()
+                && recordingBatchCache.count(index + 1) == 0)
+            {
+                callbackResult = storage->begin_getRecordingBatch(currentRecording.id, index + 1, callback);
+            }
+            else if (index > 0 && recordingBatchCache.count(index - 1) == 0)
+            {
+                callbackResult = storage->begin_getRecordingBatch(currentRecording.id, index - 1, callback);
+            }
+
+        }
+
         auto& entry = iter->second;
         entry.lastUsed = now;
         return entry.data;
     }
 
+    ARMARX_INFO << "Batch #" << index << " is not in the cache. Getting synchronously, blocking GUI...";
+
     // Entry is not in the cache, we have to get it from ArVizStorage
     auto& newEntry = recordingBatchCache[index];
     newEntry.lastUsed = now;
diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
index 3c57686177dc4e16297b9e35267cc9d3519d4602..cf6f27efd4c96d3c097c72704ac0dd8e744e3dd9 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
+++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h
@@ -45,6 +45,9 @@ namespace armarx
         Replaying,
     };
 
+    struct ArVizWidgetBatchCallback;
+
+
     /**
     \page ArmarXGui-GuiPlugins-ArViz ArViz
     \brief The ArViz allows visualizing ...
@@ -106,6 +109,8 @@ namespace armarx
         void onConnectComponent() override;
         void onDisconnectComponent() override;
 
+        void onGetBatchAsync(viz::RecordingBatch const& batch);
+
     public slots:
         /* QT slot declarations */
 
@@ -179,10 +184,15 @@ namespace armarx
 
         viz::RecordingBatch const& getRecordingBatch(long index);
 
-        std::size_t recordingBatchCacheMaxSize = 3;
+        std::size_t recordingBatchCacheMaxSize = 5;
+        std::mutex recordingBatchCacheMutex;
         std::map<long, TimestampedRecordingBatch> recordingBatchCache;
 
         ArVizWidgetMode mode = ArVizWidgetMode::NotConnected;
+
+        IceUtil::Handle<ArVizWidgetBatchCallback> callbackData;
+        armarx::viz::Callback_StorageInterface_getRecordingBatchPtr callback;
+        Ice::AsyncResultPtr callbackResult;
     };
 }