diff --git a/src/main/java/bdv/viewer/render/AccumulateProjector.java b/src/main/java/bdv/viewer/render/AccumulateProjector.java
index 980125cae767e75fc1926aa6bf60bfa83a0db365..c184991cc236c8dd4cac58a34946f1bf92ff012f 100644
--- a/src/main/java/bdv/viewer/render/AccumulateProjector.java
+++ b/src/main/java/bdv/viewer/render/AccumulateProjector.java
@@ -1,9 +1,9 @@
 package bdv.viewer.render;
 
 import java.util.ArrayList;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import net.imglib2.Cursor;
@@ -43,6 +43,8 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector
      */
     protected final int numThreads;
 
+	protected final ExecutorService executorService;
+
     /**
      * Time needed for rendering the last frame, in nano-seconds.
      */
@@ -57,7 +59,8 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector
 			final ArrayList< ? extends RandomAccessible< A > > sources,
 			final Converter< ? super A, B > converter,
 			final RandomAccessibleInterval< B > target,
-			final int numThreads )
+			final int numThreads,
+			final ExecutorService executorService )
 	{
 		this.sourceProjectors = sourceProjectors;
 		this.sources = new ArrayList< IterableInterval< A > >();
@@ -67,6 +70,7 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector
 		this.target = target;
 		this.iterableTarget = Views.flatIterable( target );
 		this.numThreads = numThreads;
+		this.executorService = executorService;
 		lastFrameRenderNanoTime = -1;
 	}
 
@@ -96,23 +100,25 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector
 		final int height = ( int ) target.dimension( 1 );
 		final int length = width * height;
 
-		final ExecutorService ex = Executors.newFixedThreadPool( numThreads );
-		final int numTasks = Math.max( numThreads * 10, height );
+		final boolean createExecutor = ( executorService == null );
+		final ExecutorService ex = createExecutor ? Executors.newFixedThreadPool( numThreads ) : executorService;
+		final int numTasks = Math.min( numThreads * 10, height );
 		final double taskLength = ( double ) length / numTasks;
 		final int numSources = sources.size();
+		final ArrayList< Callable< Void > > tasks = new ArrayList< Callable< Void > >( numTasks );
 		for ( int taskNum = 0; taskNum < numTasks; ++taskNum )
 		{
 			final int myOffset = ( int ) ( taskNum * taskLength );
 			final int myLength = ( (taskNum == numTasks - 1 ) ? length : ( int ) ( ( taskNum + 1 ) * taskLength ) ) - myOffset;
 
-			final Runnable r = new Runnable()
+			final Callable< Void > r = new Callable< Void >()
 			{
 				@SuppressWarnings( "unchecked" )
 				@Override
-				public void run()
+				public Void call()
 				{
 					if ( interrupted.get() )
-						return;
+						return null;
 
 					final Cursor< A >[] sourceCursors = new Cursor[ numSources ];
 					for ( int s = 0; s < numSources; ++s )
@@ -130,19 +136,21 @@ public abstract class AccumulateProjector< A, B > implements VolatileProjector
 							sourceCursors[ s ].fwd();
 						accumulate( sourceCursors, targetCursor.next() );
 					}
+					return null;
 				}
 			};
-			ex.execute( r );
+			tasks.add( r );
 		}
-		ex.shutdown();
 		try
 		{
-			ex.awaitTermination( 1, TimeUnit.HOURS );
+			ex.invokeAll( tasks );
 		}
 		catch ( final InterruptedException e )
 		{
 			e.printStackTrace();
 		}
+		if ( createExecutor )
+			ex.shutdown();
 
 		lastFrameRenderNanoTime = stopWatch.nanoTime();
 
diff --git a/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java b/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java
index 481a9c4189619def08de1b2ab290a385a15db7a2..4f54315de4ae4160822f8568dad5bee1d866b0f9 100644
--- a/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java
+++ b/src/main/java/bdv/viewer/render/AccumulateProjectorARGB.java
@@ -1,6 +1,7 @@
 package bdv.viewer.render;
 
 import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
 
 import net.imglib2.Cursor;
 import net.imglib2.RandomAccessible;
@@ -14,9 +15,10 @@ public class AccumulateProjectorARGB extends AccumulateProjector< ARGBType, ARGB
 			final ArrayList< VolatileProjector > sourceProjectors,
 			final ArrayList< ? extends RandomAccessible< ARGBType > > sources,
 			final RandomAccessibleInterval< ARGBType > target,
-			final int numThreads )
+			final int numThreads,
+			final ExecutorService executorService )
 	{
-		super( sourceProjectors, sources, null, target, numThreads );
+		super( sourceProjectors, sources, null, target, numThreads, executorService );
 	}
 
 	@Override
diff --git a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
index 7b4d3541b369b4bc1a59b1469058ea73fcc0c98c..e794a060b02b0cb7f7099aa9843ec723806f6c24 100644
--- a/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
+++ b/src/main/java/bdv/viewer/render/MultiResolutionRenderer.java
@@ -571,7 +571,7 @@ public class MultiResolutionRenderer
 					sourceProjectors.add( p );
 					sourceImages.add( renderImage );
 				}
-				projector = new AccumulateProjectorARGB( sourceProjectors, sourceImages, screenImage, numRenderingThreads );
+				projector = new AccumulateProjectorARGB( sourceProjectors, sourceImages, screenImage, numRenderingThreads, renderingExecutorService );
 			}
 			previousTimepoint = viewerState.getCurrentTimepoint();
 			cache.initIoTimeBudget( iobudget );