diff --git a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
index f9f05a25fef3cda8d1371341ee6021297b346f16..d2f21d4db5bbe17238e4b0e4b8cf9c949a6a7e22 100644
--- a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
+++ b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
@@ -141,8 +141,12 @@ public class MultiResolutionRenderer
 		 */
 		final AffineTransform3D scale;
 
+		private final double screenToViewerScale;
+
 		public ScreenScale( final int screenW, final int screenH, final double screenToViewerScale )
 		{
+			this.screenToViewerScale = screenToViewerScale;
+
 			w = ( int ) Math.ceil( screenToViewerScale * screenW );
 			h = ( int ) Math.ceil( screenToViewerScale * screenH );
 			scale = new AffineTransform3D();
@@ -151,6 +155,37 @@ public class MultiResolutionRenderer
 			scale.set( 0.5 * screenToViewerScale - 0.5, 0, 3 );
 			scale.set( 0.5 * screenToViewerScale - 0.5, 1, 3 );
 		}
+
+		Interval requestedScreenInterval = null;
+
+		public void requestInterval( final Interval screenInterval )
+		{
+			requestedScreenInterval = requestedScreenInterval == null ? screenInterval : Intervals.union( requestedScreenInterval, screenInterval );
+		}
+
+		public Interval pullScreenInterval()
+		{
+			final Interval interval = requestedScreenInterval;
+			requestedScreenInterval = null;
+			return interval;
+		}
+
+		public double estimateIntervalRenderNanos( final double renderNanosPerPixel )
+		{
+			return renderNanosPerPixel * Intervals.numElements( scaleScreenInterval( requestedScreenInterval ) );
+		}
+
+		public Interval scaleScreenInterval( final Interval requestedScreenInterval )
+		{
+			// This is equivalent to
+			// Intervals.intersect( new FinalInterval( w, h ), Intervals.smallestContainingInterval( Intervals.scale( requestedScreenInterval, screenToViewerScale ) ) );
+			return Intervals.createMinMax(
+					Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 0 ) * screenToViewerScale ) ),
+					Math.max( 0, ( int ) Math.floor( requestedScreenInterval.min( 1 ) * screenToViewerScale ) ),
+					Math.min( w - 1, ( int ) Math.ceil( requestedScreenInterval.max( 0 ) * screenToViewerScale ) ),
+					Math.min( h - 1, ( int ) Math.ceil( requestedScreenInterval.max( 1 ) * screenToViewerScale ) )
+			);
+		}
 	}
 
 	private final ScreenScale[] screenScales;
@@ -479,12 +514,30 @@ public class MultiResolutionRenderer
 	{
 		if ( renderingMayBeCancelled && projector != null )
 			projector.cancel();
+		for ( ScreenScale screenScale : screenScales )
+			if ( screenScale != null )
+				screenScale.pullScreenInterval();
+		intervalMode = false;
 		newFrameRequest = true;
-				requestedScreenInterval = null;
-				intervalMode = false;
 		painterThread.requestRepaint();
 	}
 
+	public synchronized void requestRepaint( final Interval screenInterval )
+	{
+		if ( renderingMayBeCancelled || intervalMode )
+		{
+			if ( projector != null )
+				projector.cancel();
+			for ( final ScreenScale screenScale : screenScales )
+				screenScale.requestInterval( screenInterval );
+			intervalMode = true;
+			newIntervalRequest = true;
+			painterThread.requestRepaint();
+		}
+		else
+			requestRepaint();
+	}
+
 	private int suggestScreenScale( final Dimensions screenSize, final int numSources )
 	{
 		final double intervalRenderNanos = renderNanosPerPixelAndSource.getAverage() * Intervals.numElements( screenSize ) * numSources;
@@ -547,7 +600,6 @@ public class MultiResolutionRenderer
 
 	// =========== intervals =============
 
-	private Interval requestedScreenInterval;
 	private RenderResult currentRenderResult;
 
 	public static class IntervalRenderData
@@ -574,31 +626,45 @@ public class MultiResolutionRenderer
 
 	private boolean paintInterval( final boolean newInterval )
 	{
-		if ( newInterval )
-		{
-			cacheControl.prepareNextFrame();
-			requestedIntervalScaleIndex = Math.max( currentScreenScaleIndex, suggestScreenScale( requestedScreenInterval, currentNumVisibleSources ) );
-		}
-
-		final boolean createProjector = newInterval || ( requestedIntervalScaleIndex != currentIntervalScaleIndex );
-
+		final boolean createProjector;
+		final ScreenScale screenScale;
+		final Interval requestedScreenInterval;
 		final VolatileProjector p;
 
 		synchronized ( this )
 		{
+			if ( newInterval )
+			{
+				cacheControl.prepareNextFrame();
+				final double renderNanosPerPixel = currentNumVisibleSources * renderNanosPerPixelAndSource.getAverage();
+				for ( int i = currentScreenScaleIndex; i < screenScales.length; i++ )
+				{
+					final double renderTime = screenScales[ i ].estimateIntervalRenderNanos( renderNanosPerPixel );
+					if ( renderTime <= targetRenderNanos )
+					{
+						requestedIntervalScaleIndex = i;
+						break;
+					}
+				}
+			}
+
+			createProjector = newInterval || ( requestedIntervalScaleIndex != currentIntervalScaleIndex );
+			screenScale = screenScales[ requestedIntervalScaleIndex ];
+			requestedScreenInterval = screenScale.pullScreenInterval();
+
 			if ( createProjector )
 			{
-				final Interval interval = scaleScreenInterval( requestedScreenInterval, requestedIntervalScaleIndex );
+				final Interval interval = screenScale.scaleScreenInterval( requestedScreenInterval );
 				intervalResult.init(
 						( int ) interval.dimension( 0 ),
 						( int ) interval.dimension( 1 ) );
-				final double intervalScale = screenScaleFactors[ requestedIntervalScaleIndex ];
+				final double intervalScale = screenScale.screenToViewerScale;
 				intervalResult.setScaleFactor( intervalScale );
 				final double offsetX = interval.min( 0 );
 				final double offsetY = interval.min( 1 );
 				projector = createProjector( currentViewerState, requestedIntervalScaleIndex, intervalResult.getScreenImage(), offsetX, offsetY );
 
-				final Interval targetInterval = scaleScreenInterval( requestedScreenInterval, currentScreenScaleIndex );
+				final Interval targetInterval = screenScales[ currentScreenScaleIndex ].scaleScreenInterval( requestedScreenInterval );
 				final double relativeScale = screenScaleFactors[ currentScreenScaleIndex ] / intervalScale;
 				final double tx = interval.min( 0 ) * relativeScale;
 				final double ty = interval.min( 1 ) * relativeScale;
@@ -645,9 +711,14 @@ public class MultiResolutionRenderer
 						// restore interrupted state
 						Thread.currentThread().interrupt();
 					}
+					screenScale.requestInterval( requestedScreenInterval );
 					iterateRepaintInterval( currentIntervalScaleIndex );
 				}
 			}
+			else
+			{
+				screenScale.requestInterval( requestedScreenInterval );
+			}
 		}
 
 		return success;
@@ -673,19 +744,4 @@ public class MultiResolutionRenderer
 				Math.min( clipH - 1, ( int ) Math.ceil( requestedScreenInterval.max( 1 ) * scale ) )
 		);
 	}
-
-	public synchronized void requestRepaint( final Interval screenInterval )
-	{
-		if ( renderingMayBeCancelled || intervalMode )
-		{
-			if ( projector != null )
-				projector.cancel();
-			requestedScreenInterval = screenInterval;
-			intervalMode = true;
-			newIntervalRequest = true;
-			painterThread.requestRepaint();
-		}
-		else
-			requestRepaint();
-	}
 }