diff --git a/pom.xml b/pom.xml
index 2b5112dab45ce3b373e2aaa9d4d4e18b54b8fc4f..227489f7f909dd09808929d390e8a47f867f5adf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
 		<dependency>
 			<groupId>sc.fiji</groupId>
 			<artifactId>bigdataviewer-core</artifactId>
+			<version>1.0.3-SNAPSHOT</version>
 		</dependency>
 		<dependency>
 			<groupId>net.imagej</groupId>
diff --git a/src/main/java/bdv/ij/ExportImagePlusPlugIn.java b/src/main/java/bdv/ij/ExportImagePlusPlugIn.java
index 6a09940bef4659902a1d7029fd45bdd2b9117ed4..4b9510b83540a20ad170dd0738021a73d3431253 100644
--- a/src/main/java/bdv/ij/ExportImagePlusPlugIn.java
+++ b/src/main/java/bdv/ij/ExportImagePlusPlugIn.java
@@ -19,7 +19,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
-import mpicbg.spim.data.generic.sequence.BasicImgLoader;
 import mpicbg.spim.data.generic.sequence.BasicViewSetup;
 import mpicbg.spim.data.registration.ViewRegistration;
 import mpicbg.spim.data.registration.ViewRegistrations;
@@ -28,13 +27,15 @@ import mpicbg.spim.data.sequence.FinalVoxelDimensions;
 import mpicbg.spim.data.sequence.TimePoint;
 import mpicbg.spim.data.sequence.TimePoints;
 import net.imglib2.FinalDimensions;
+import net.imglib2.RandomAccessibleInterval;
 import net.imglib2.realtransform.AffineTransform3D;
-import net.imglib2.type.numeric.integer.UnsignedShortType;
 import bdv.export.ExportMipmapInfo;
 import bdv.export.ProgressWriter;
 import bdv.export.ProposeMipmaps;
 import bdv.export.SubTaskProgressWriter;
 import bdv.export.WriteSequenceToHdf5;
+import bdv.export.WriteSequenceToHdf5.AfterEachPlane;
+import bdv.export.WriteSequenceToHdf5.LoopbackHeuristic;
 import bdv.ij.export.imgloader.ImagePlusImgLoader;
 import bdv.ij.export.imgloader.ImagePlusImgLoader.MinMaxOption;
 import bdv.ij.util.PluginHelper;
@@ -116,7 +117,7 @@ public class ExportImagePlusPlugIn implements PlugIn
 		progressWriter.out().println( "starting export..." );
 
 		// create ImgLoader wrapping the image
-		final BasicImgLoader< UnsignedShortType > imgLoader;
+		final ImagePlusImgLoader< ? > imgLoader;
 		switch ( imp.getType() )
 		{
 		case ImagePlus.GRAY8:
@@ -157,6 +158,57 @@ public class ExportImagePlusPlugIn implements PlugIn
 		for ( final BasicViewSetup setup : seq.getViewSetupsOrdered() )
 			perSetupExportMipmapInfo.put( setup.getId(), mipmapInfo );
 
+		// LoopBackHeuristic:
+		// - If saving more than 8x on pixel reads use the loopback image over
+		//   original image
+		// - For virtual stacks also consider the cache size that would be
+		//   required for all original planes contributing to a "plane of
+		//   blocks" at the current level. If this is more than 1/4 of
+		//   available memory, use the loopback image.
+		final boolean isVirtual = imp.getStack().isVirtual();
+		final long planeSizeInBytes = imp.getWidth() * imp.getHeight() * imp.getBytesPerPixel();
+		final long ijMaxMemory = IJ.maxMemory();
+		final LoopbackHeuristic loopbackHeuristic = new LoopbackHeuristic()
+		{
+			@Override
+			public boolean decide( final RandomAccessibleInterval< ? > originalImg, final int[] factorsToOriginalImg, final int previousLevel, final int[] factorsToPreviousLevel, final int[] chunkSize )
+			{
+				if ( previousLevel < 0 )
+					return false;
+
+				if ( WriteSequenceToHdf5.numElements( factorsToOriginalImg ) / WriteSequenceToHdf5.numElements( factorsToPreviousLevel ) >= 8 )
+					return true;
+
+				if ( isVirtual )
+				{
+					final long requiredCacheSize = planeSizeInBytes * factorsToOriginalImg[ 2 ] * chunkSize[ 2 ];
+					if ( requiredCacheSize > ijMaxMemory / 4 )
+						return true;
+				}
+
+				return false;
+			}
+		};
+
+		final AfterEachPlane afterEachPlane = new AfterEachPlane()
+		{
+			@Override
+			public void afterEachPlane( final boolean usedLoopBack )
+			{
+				if ( !usedLoopBack && isVirtual )
+				{
+					final long free = Runtime.getRuntime().freeMemory();
+					final long total = Runtime.getRuntime().totalMemory();
+					final long max = Runtime.getRuntime().maxMemory();
+					final long actuallyFree = max - total + free;
+
+					if ( actuallyFree < max / 2 )
+						imgLoader.clearCache();
+				}
+			}
+
+		};
+
 		final ArrayList< Partition > partitions;
 		if ( params.split )
 		{
@@ -168,14 +220,14 @@ public class ExportImagePlusPlugIn implements PlugIn
 			{
 				final Partition partition = partitions.get( i );
 				final ProgressWriter p = new SubTaskProgressWriter( progressWriter, 0, 0.95 * i / partitions.size() );
-				WriteSequenceToHdf5.writeHdf5PartitionFile( seq, perSetupExportMipmapInfo, params.deflate, partition, p );
+				WriteSequenceToHdf5.writeHdf5PartitionFile( seq, perSetupExportMipmapInfo, params.deflate, partition, loopbackHeuristic, afterEachPlane, p );
 			}
 			WriteSequenceToHdf5.writeHdf5PartitionLinkFile( seq, perSetupExportMipmapInfo, partitions, params.hdf5File );
 		}
 		else
 		{
 			partitions = null;
-			WriteSequenceToHdf5.writeHdf5File( seq, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progressWriter, 0, 0.95 ) );
+			WriteSequenceToHdf5.writeHdf5File( seq, perSetupExportMipmapInfo, params.deflate, params.hdf5File, loopbackHeuristic, afterEachPlane, new SubTaskProgressWriter( progressWriter, 0, 0.95 ) );
 		}
 
 		// write xml sequence description
diff --git a/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java b/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java
index 6f86470732fd1f4b9fda6e2a778a3130888d47ce..7fa3a509a512cdac21ba39cf0fb02e90e874f28c 100644
--- a/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java
+++ b/src/main/java/bdv/ij/ExportSpimFusionPlugIn.java
@@ -294,7 +294,7 @@ public class ExportSpimFusionPlugIn implements PlugIn
 		for ( final Partition partition : newPartitions )
 		{
 			final SubTaskProgressWriter subtaskProgress = new SubTaskProgressWriter( progress, complete, complete + completionStep );
-			WriteSequenceToHdf5.writeHdf5PartitionFile( fusionSeq, perSetupExportMipmapInfo, params.deflate, partition, subtaskProgress );
+			WriteSequenceToHdf5.writeHdf5PartitionFile( fusionSeq, perSetupExportMipmapInfo, params.deflate, partition, null, null, subtaskProgress );
 			complete += completionStep;
 		}
 
@@ -346,13 +346,13 @@ public class ExportSpimFusionPlugIn implements PlugIn
 			{
 				final Partition partition = partitions.get( i );
 				final ProgressWriter p = new SubTaskProgressWriter( progress, 0, 0.95 * i / partitions.size() );
-				WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, p );
+				WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, null, null, p );
 			}
 			WriteSequenceToHdf5.writeHdf5PartitionLinkFile( desc, perSetupExportMipmapInfo, partitions, params.hdf5File );
 		}
 		else
 		{
-			WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progress, 0, 0.95 ) );
+			WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, null, null, new SubTaskProgressWriter( progress, 0, 0.95 ) );
 		}
 
 		// write xml file
diff --git a/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java b/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java
index 7b1e2e514c86ddc68ab337bd9c91ad630e3ce673..7a1747b9270f7564a8f58b456ddb13ed02a5ff3a 100644
--- a/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java
+++ b/src/main/java/bdv/ij/ExportSpimSequencePlugIn.java
@@ -86,14 +86,14 @@ public class ExportSpimSequencePlugIn implements PlugIn
 			{
 				final Partition partition = partitions.get( i );
 				final ProgressWriter p = new SubTaskProgressWriter( progress, 0, 0.95 * i / partitions.size() );
-				WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, p );
+				WriteSequenceToHdf5.writeHdf5PartitionFile( desc, perSetupExportMipmapInfo, params.deflate, partition, null, null, p );
 			}
 			WriteSequenceToHdf5.writeHdf5PartitionLinkFile( desc, perSetupExportMipmapInfo, partitions, params.hdf5File );
 		}
 		else
 		{
 			partitions = null;
-			WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, new SubTaskProgressWriter( progress, 0, 0.95 ) );
+			WriteSequenceToHdf5.writeHdf5File( desc, perSetupExportMipmapInfo, params.deflate, params.hdf5File, null, null, new SubTaskProgressWriter( progress, 0, 0.95 ) );
 		}
 
 		final Hdf5ImageLoader loader = new Hdf5ImageLoader( params.hdf5File, partitions, null, false );
diff --git a/src/main/java/bdv/ij/OpenImagePlusPlugIn.java b/src/main/java/bdv/ij/OpenImagePlusPlugIn.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dd8282bc724b1268edec23e2efca1a182f0c818
--- /dev/null
+++ b/src/main/java/bdv/ij/OpenImagePlusPlugIn.java
@@ -0,0 +1,222 @@
+package bdv.ij;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import bdv.BigDataViewer;
+import bdv.ij.util.ProgressWriterIJ;
+import bdv.img.imagestack.ImageStackImageLoader;
+import bdv.img.virtualstack.VirtualStackImageLoader;
+import bdv.spimdata.SequenceDescriptionMinimal;
+import bdv.spimdata.SpimDataMinimal;
+import bdv.spimdata.WrapBasicImgLoader;
+import bdv.tools.brightness.ConverterSetup;
+import bdv.tools.brightness.SetupAssignments;
+import bdv.viewer.DisplayMode;
+import bdv.viewer.VisibilityAndGrouping;
+import ij.CompositeImage;
+import ij.IJ;
+import ij.ImageJ;
+import ij.ImagePlus;
+import ij.WindowManager;
+import ij.plugin.PlugIn;
+import ij.process.LUT;
+import mpicbg.spim.data.SpimDataException;
+import mpicbg.spim.data.generic.sequence.BasicImgLoader;
+import mpicbg.spim.data.generic.sequence.BasicViewSetup;
+import mpicbg.spim.data.registration.ViewRegistration;
+import mpicbg.spim.data.registration.ViewRegistrations;
+import mpicbg.spim.data.sequence.Channel;
+import mpicbg.spim.data.sequence.FinalVoxelDimensions;
+import mpicbg.spim.data.sequence.TimePoint;
+import mpicbg.spim.data.sequence.TimePoints;
+import net.imglib2.FinalDimensions;
+import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.type.numeric.ARGBType;
+
+/**
+ * ImageJ plugin to show the current image in BigDataViewer.
+ *
+ * @author Tobias Pietzsch <tobias.pietzsch@gmail.com>
+ */
+public class OpenImagePlusPlugIn implements PlugIn
+{
+	public static void main( final String[] args )
+	{
+		System.setProperty( "apple.laf.useScreenMenuBar", "true" );
+		new ImageJ();
+		IJ.run("Confocal Series (2.2MB)");
+//		IJ.run("Fly Brain (1MB)");
+		new OpenImagePlusPlugIn().run( null );
+	}
+
+	@Override
+	public void run( final String arg )
+	{
+		// get the current image
+		final ImagePlus imp = WindowManager.getCurrentImage();
+
+		// make sure there is one
+		if ( imp == null )
+		{
+			IJ.showMessage( "Please open an image first." );
+			return;
+		}
+
+		// check the image type
+		switch ( imp.getType() )
+		{
+		case ImagePlus.GRAY8:
+		case ImagePlus.GRAY16:
+		case ImagePlus.GRAY32:
+		case ImagePlus.COLOR_RGB:
+			break;
+		default:
+			IJ.showMessage( "Only 8, 16, 32-bit images and RGB images are supported currently!" );
+			return;
+		}
+
+		// check the image dimensionality
+		if ( imp.getNDimensions() < 3 )
+		{
+			IJ.showMessage( "Image must be at least 3-dimensional!" );
+			return;
+		}
+
+		// get calibration and image size
+		final double pw = imp.getCalibration().pixelWidth;
+		final double ph = imp.getCalibration().pixelHeight;
+		final double pd = imp.getCalibration().pixelDepth;
+		String punit = imp.getCalibration().getUnit();
+		if ( punit == null || punit.isEmpty() )
+			punit = "px";
+		final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd );
+		final int w = imp.getWidth();
+		final int h = imp.getHeight();
+		final int d = imp.getNSlices();
+		final FinalDimensions size = new FinalDimensions( new int[] { w, h, d } );
+
+		// propose reasonable mipmap settings
+//		final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) );
+
+//		imp.getDisplayRangeMin();
+//		imp.getDisplayRangeMax();
+
+		// create ImgLoader wrapping the image
+		final BasicImgLoader< ? > imgLoader;
+		if ( imp.getStack().isVirtual() )
+		{
+			switch ( imp.getType() )
+			{
+			case ImagePlus.GRAY8:
+				imgLoader = VirtualStackImageLoader.createUnsignedByteInstance( imp );
+				break;
+			case ImagePlus.GRAY16:
+				imgLoader = VirtualStackImageLoader.createUnsignedShortInstance( imp );
+				break;
+			case ImagePlus.GRAY32:
+				imgLoader = VirtualStackImageLoader.createFloatInstance( imp );
+				break;
+			case ImagePlus.COLOR_RGB:
+			default:
+				imgLoader = VirtualStackImageLoader.createARGBInstance( imp );
+				break;
+			}
+		}
+		else
+		{
+			switch ( imp.getType() )
+			{
+			case ImagePlus.GRAY8:
+				imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp );
+				break;
+			case ImagePlus.GRAY16:
+				imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp );
+				break;
+			case ImagePlus.GRAY32:
+				imgLoader = ImageStackImageLoader.createFloatInstance( imp );
+				break;
+			case ImagePlus.COLOR_RGB:
+			default:
+				imgLoader = ImageStackImageLoader.createARGBInstance( imp );
+				break;
+			}
+		}
+
+		final int numTimepoints = imp.getNFrames();
+		final int numSetups = imp.getNChannels();
+
+		// create setups from channels
+		final HashMap< Integer, BasicViewSetup > setups = new HashMap< Integer, BasicViewSetup >( numSetups );
+		for ( int s = 0; s < numSetups; ++s )
+		{
+			final BasicViewSetup setup = new BasicViewSetup( s, String.format( "channel %d", s + 1 ), size, voxelSize );
+			setup.setAttribute( new Channel( s + 1 ) );
+			setups.put( s, setup );
+		}
+
+		// create timepoints
+		final ArrayList< TimePoint > timepoints = new ArrayList< TimePoint >( numTimepoints );
+		for ( int t = 0; t < numTimepoints; ++t )
+			timepoints.add( new TimePoint( t ) );
+		final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null );
+
+		// create ViewRegistrations from the images calibration
+		final AffineTransform3D sourceTransform = new AffineTransform3D();
+		sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 );
+		final ArrayList< ViewRegistration > registrations = new ArrayList< ViewRegistration >();
+		for ( int t = 0; t < numTimepoints; ++t )
+			for ( int s = 0; s < numSetups; ++s )
+				registrations.add( new ViewRegistration( t, s, sourceTransform ) );
+
+		final File basePath = new File(".");
+		final SpimDataMinimal spimData = new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) );
+		WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData );
+
+		try
+		{
+			final BigDataViewer bdv = new BigDataViewer( spimData, "BigDataViewer", new ProgressWriterIJ() );
+			final SetupAssignments sa = bdv.getSetupAssignments();
+			final VisibilityAndGrouping vg = bdv.getViewer().getVisibilityAndGrouping();
+			if ( imp.isComposite() )
+				transferChannelSettings( ( CompositeImage ) imp, sa, vg );
+			else if ( imp.getType() == ImagePlus.COLOR_RGB )
+				transferSettingsRGB( imp, sa );
+		}
+		catch ( final SpimDataException e )
+		{
+			throw new RuntimeException( e );
+		}
+	}
+
+	protected void transferChannelSettings( final CompositeImage ci, final SetupAssignments setupAssignments, final VisibilityAndGrouping visibility )
+	{
+		final int nChannels = ci.getNChannels();
+		final int mode = ci.getCompositeMode();
+		final boolean transferColor = mode == IJ.COMPOSITE || mode == IJ.COLOR;
+		for ( int c = 0; c < nChannels; ++c )
+		{
+			final LUT lut = ci.getChannelLut( c + 1 );
+			final ConverterSetup setup = setupAssignments.getConverterSetups().get( c );
+			if ( transferColor )
+				setup.setColor( new ARGBType( lut.getRGB( 255 ) ) );
+			setup.setDisplayRange( lut.min, lut.max );
+		}
+		if ( mode == IJ.COMPOSITE )
+		{
+			final boolean[] activeChannels = ci.getActiveChannels();
+			visibility.setDisplayMode( DisplayMode.FUSED );
+			for ( int i = 0; i < activeChannels.length; ++i )
+				visibility.setSourceActive( i, activeChannels[ i ] );
+		}
+		else
+			visibility.setDisplayMode( DisplayMode.SINGLE );
+		visibility.setCurrentSource( ci.getChannel() - 1 );
+	}
+
+	protected void transferSettingsRGB( final ImagePlus imp, final SetupAssignments setupAssignments )
+	{
+		final ConverterSetup setup = setupAssignments.getConverterSetups().get( 0 );
+		setup.setDisplayRange( imp.getDisplayRangeMin(), imp.getDisplayRangeMax() );
+	}
+}
diff --git a/src/main/java/bdv/ij/export/Scripting.java b/src/main/java/bdv/ij/export/Scripting.java
index 38ac2431bda86ae493f2c3325424dafe2a3aaadc..dc8f190cc3d9493e024aa9d7525a76b1730069b7 100644
--- a/src/main/java/bdv/ij/export/Scripting.java
+++ b/src/main/java/bdv/ij/export/Scripting.java
@@ -203,7 +203,7 @@ public class Scripting
 		public void writePartition( final int index )
 		{
 			if ( index >= 0 && index < partitions.size() )
-				WriteSequenceToHdf5.writeHdf5PartitionFile( spimData.getSequenceDescription(), perSetupMipmapInfo, deflate, partitions.get( index ), null );
+				WriteSequenceToHdf5.writeHdf5PartitionFile( spimData.getSequenceDescription(), perSetupMipmapInfo, deflate, partitions.get( index ), null, null, null );
 		}
 
 		public void writeXmlAndLinks() throws SpimDataException
diff --git a/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java b/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java
index 2535261b90ff6159f26f84f41873d59ecd913fb3..fbefc4ad89bcf786835780ca1350b1ac408e9a93 100644
--- a/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java
+++ b/src/main/java/bdv/ij/export/imgloader/ImagePlusImgLoader.java
@@ -12,6 +12,8 @@ import net.imglib2.type.numeric.RealType;
 import net.imglib2.type.numeric.integer.UnsignedByteType;
 import net.imglib2.type.numeric.integer.UnsignedShortType;
 import net.imglib2.type.numeric.real.FloatType;
+import bdv.img.cache.VolatileGlobalCellCache;
+import bdv.img.imagestack.ImageStackImageLoader;
 import bdv.img.virtualstack.VirtualStackImageLoader;
 
 /**
@@ -40,33 +42,45 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp
 	{
 		if( imp.getType() != ImagePlus.GRAY8 )
 			throw new RuntimeException( "expected ImagePlus type GRAY8" );
-		return new ImagePlusImgLoader< UnsignedByteType >( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max );
+		if ( imp.getStack() != null && imp.getStack().isVirtual() )
+			return new ImagePlusImgLoader< UnsignedByteType >( imp, VirtualStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max );
+		else
+			return new ImagePlusImgLoader< UnsignedByteType >( imp, ImageStackImageLoader.createUnsignedByteInstance( imp ), minMaxOption, min, max );
 	}
 
 	public static ImagePlusImgLoader< UnsignedShortType > createGray16( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max )
 	{
 		if( imp.getType() != ImagePlus.GRAY16 )
 			throw new RuntimeException( "expected ImagePlus type GRAY16" );
-		return new ImagePlusImgLoader< UnsignedShortType >( imp, VirtualStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max );
+		if ( imp.getStack() != null && imp.getStack().isVirtual() )
+			return new ImagePlusImgLoader< UnsignedShortType >( imp, VirtualStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max );
+		else
+			return new ImagePlusImgLoader< UnsignedShortType >( imp, ImageStackImageLoader.createUnsignedShortInstance( imp ), minMaxOption, min, max );
 	}
 
 	public static ImagePlusImgLoader< FloatType > createGray32( final ImagePlus imp, final MinMaxOption minMaxOption, final double min, final double max )
 	{
 		if( imp.getType() != ImagePlus.GRAY32 )
 			throw new RuntimeException( "expected ImagePlus type GRAY32" );
-		return new ImagePlusImgLoader< FloatType >( imp, VirtualStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max );
+		if ( imp.getStack() != null && imp.getStack().isVirtual() )
+			return new ImagePlusImgLoader< FloatType >( imp, VirtualStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max );
+		else
+			return new ImagePlusImgLoader< FloatType >( imp, ImageStackImageLoader.createFloatInstance( imp ), minMaxOption, min, max );
 	}
 
 	protected final ImagePlus imp;
 
-	protected final VirtualStackImageLoader< T, ?, ? > loader;
+	protected final BasicImgLoader< T > loader;
+
+	protected VolatileGlobalCellCache< ? > loadercache;
 
 	protected double impMin;
 
 	protected double impMax;
 
+	@SuppressWarnings( "unchecked" )
 	protected ImagePlusImgLoader( final ImagePlus imp,
-			final VirtualStackImageLoader< T, ?, ? > loader,
+			final BasicImgLoader< T > loader,
 			final MinMaxOption minMaxOption,
 			final double min,
 			final double max )
@@ -74,6 +88,11 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp
 		this.imp = imp;
 		this.loader = loader;
 
+		if ( loader instanceof VirtualStackImageLoader )
+			this.loadercache = ( ( VirtualStackImageLoader< T, ?, ? > ) loader ).getCache();
+		else
+			this.loadercache = null;
+
 		if ( minMaxOption == MinMaxOption.COMPUTE )
 		{
 			impMin = Double.POSITIVE_INFINITY;
@@ -88,7 +107,8 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp
 					ComputeMinMax.computeMinMax( loader.getImage( new ViewId( t, s ) ), minT, maxT );
 					impMin = Math.min( minT.getRealDouble(), impMin );
 					impMax = Math.max( maxT.getRealDouble(), impMax );
-					loader.getCache().clearCache();
+					if ( loadercache != null )
+						loadercache.clearCache();
 				}
 			System.out.println( "COMPUTE" );
 			System.out.println( impMin + "  " + impMax );
@@ -109,10 +129,21 @@ public class ImagePlusImgLoader< T extends RealType< T > & NativeType< T > > imp
 		}
 	}
 
+	public void clearCache()
+	{
+		if ( loadercache != null )
+		{
+			loadercache.clearCache();
+			System.runFinalization();
+			System.gc();
+		}
+	}
+
 	@Override
 	public RandomAccessibleInterval< UnsignedShortType > getImage( final ViewId view )
 	{
-		loader.getCache().clearCache();
+		if ( loadercache != null )
+			loadercache.clearCache();
 		final RandomAccessibleInterval< T > img = loader.getImage( view );
 		return Converters.convert( img, new RealUnsignedShortConverter< T >( impMin, impMax ), new UnsignedShortType() );
 	}
diff --git a/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java b/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java
index 0625243aec2939f7e91178aefc69ddcbd0f6c880..069bdf922a244f6941d300109b542914a82dbf5d 100644
--- a/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java
+++ b/src/main/java/bdv/ij/export/tiles/CellVoyagerDataExporter.java
@@ -315,7 +315,7 @@ public class CellVoyagerDataExporter
 		 * Write to HDF5
 		 */
 
-		WriteSequenceToHdf5.writeHdf5File( sequenceDescriptionHDF5, resolutions, chunks, true, hdf5File, progressWriter );
+		WriteSequenceToHdf5.writeHdf5File( sequenceDescriptionHDF5, resolutions, chunks, true, hdf5File, null, null, progressWriter );
 
 		/*
 		 * write XML sequence description
diff --git a/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java b/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3014bb52f50eef2cf73fd2f70e94a59598d4026
--- /dev/null
+++ b/src/main/java/bdv/img/imagestack/ImageStackImageLoader.java
@@ -0,0 +1,133 @@
+package bdv.img.imagestack;
+
+import ij.ImagePlus;
+import mpicbg.spim.data.generic.sequence.BasicImgLoader;
+import mpicbg.spim.data.sequence.ViewId;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.basictypeaccess.array.ArrayDataAccess;
+import net.imglib2.img.basictypeaccess.array.ByteArray;
+import net.imglib2.img.basictypeaccess.array.FloatArray;
+import net.imglib2.img.basictypeaccess.array.IntArray;
+import net.imglib2.img.basictypeaccess.array.ShortArray;
+import net.imglib2.img.planar.PlanarImg;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.numeric.ARGBType;
+import net.imglib2.type.numeric.NumericType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.type.numeric.integer.UnsignedShortType;
+import net.imglib2.type.numeric.real.FloatType;
+
+public abstract class ImageStackImageLoader< T extends NumericType< T > & NativeType< T >, A extends ArrayDataAccess< A > > implements BasicImgLoader< T >
+{
+	public static ImageStackImageLoader< UnsignedByteType, ByteArray > createUnsignedByteInstance( final ImagePlus imp )
+	{
+		return new ImageStackImageLoader< UnsignedByteType, ByteArray >( new UnsignedByteType(), imp )
+		{
+			@Override
+			protected ByteArray wrapPixels( final Object array )
+			{
+				return new ByteArray( ( byte[] ) array );
+			}
+
+			@Override
+			protected void linkType( final PlanarImg< UnsignedByteType, ByteArray > img )
+			{
+				img.setLinkedType( new UnsignedByteType( img ) );
+			}
+		};
+	}
+
+	public static ImageStackImageLoader< UnsignedShortType, ShortArray > createUnsignedShortInstance( final ImagePlus imp )
+	{
+		return new ImageStackImageLoader< UnsignedShortType, ShortArray >( new UnsignedShortType(), imp )
+		{
+			@Override
+			protected ShortArray wrapPixels( final Object array )
+			{
+				return new ShortArray( ( short[] ) array );
+			}
+
+			@Override
+			protected void linkType( final PlanarImg< UnsignedShortType, ShortArray > img )
+			{
+				img.setLinkedType( new UnsignedShortType( img ) );
+			}
+		};
+	}
+
+	public static ImageStackImageLoader< FloatType, FloatArray > createFloatInstance( final ImagePlus imp )
+	{
+		return new ImageStackImageLoader< FloatType, FloatArray >( new FloatType(), imp )
+		{
+			@Override
+			protected FloatArray wrapPixels( final Object array )
+			{
+				return new FloatArray( ( float[] ) array );
+			}
+
+			@Override
+			protected void linkType( final PlanarImg< FloatType, FloatArray > img )
+			{
+				img.setLinkedType( new FloatType( img ) );
+			}
+		};
+	}
+
+	public static ImageStackImageLoader< ARGBType, IntArray > createARGBInstance( final ImagePlus imp )
+	{
+		return new ImageStackImageLoader< ARGBType, IntArray >( new ARGBType(), imp )
+		{
+			@Override
+			protected IntArray wrapPixels( final Object array )
+			{
+				return new IntArray( ( int[] ) array );
+			}
+
+			@Override
+			protected void linkType( final PlanarImg< ARGBType, IntArray > img )
+			{
+				img.setLinkedType( new ARGBType( img ) );
+			}
+		};
+	}
+
+	private final T type;
+
+	private final ImagePlus imp;
+
+	private final long[] dim;
+
+	public ImageStackImageLoader( final T type, final ImagePlus imp )
+	{
+		this.type = type;
+		this.imp = imp;
+		this.dim = new long[] { imp.getWidth(), imp.getHeight(), imp.getNSlices() };
+	}
+
+	protected abstract A wrapPixels( Object array );
+
+	protected abstract void linkType( PlanarImg< T, A > img );
+
+	@Override
+	public RandomAccessibleInterval< T > getImage( final ViewId view )
+	{
+		return new PlanarImg< T, A >( dim, type.getEntitiesPerPixel() )
+		{
+			private PlanarImg< T, A > init()
+			{
+				final int channel = view.getViewSetupId() + 1;
+				final int frame = view.getTimePointId() + 1;
+				for ( int slice = 1; slice <= dim[ 2 ]; ++slice )
+					mirror.set( slice - 1, wrapPixels( imp.getStack().getPixels( imp.getStackIndex( channel, slice, frame ) ) ) );
+				linkType( this );
+				return this;
+			}
+		}.init();
+	}
+
+	@Override
+	public T getImageType()
+	{
+		return type;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java b/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java
index 19b30b5a3c79bd2eb88839bd11933bcb81e415c1..4d2f27000a020b97176f735b4f1d13119fccd483 100644
--- a/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java
+++ b/src/main/java/bdv/img/virtualstack/VirtualStackImageLoader.java
@@ -8,6 +8,7 @@ import net.imglib2.img.NativeImg;
 import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess;
 import net.imglib2.img.basictypeaccess.volatiles.array.VolatileByteArray;
 import net.imglib2.img.basictypeaccess.volatiles.array.VolatileFloatArray;
+import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray;
 import net.imglib2.img.basictypeaccess.volatiles.array.VolatileShortArray;
 import net.imglib2.realtransform.AffineTransform3D;
 import net.imglib2.type.NativeType;
@@ -37,7 +38,7 @@ import bdv.img.cache.VolatileImgCells.CellCache;
  * Use {@link #createFloatInstance(ImagePlus)},
  * {@link #createUnsignedByteInstance(ImagePlus)} or
  * {@link #createUnsignedShortInstance(ImagePlus)} depending on the ImagePlus
- * pixel type. ARGB is currently not supported.
+ * pixel type.
  *
  * When {@link #getImage(ViewId) loading images}, the provided setup id is used
  * as the channel index of the {@link ImagePlus}, the provided timepoint id is
@@ -112,6 +113,25 @@ public abstract class VirtualStackImageLoader< T extends NativeType< T >, V exte
 		};
 	}
 
+	public static VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray > createARGBInstance( final ImagePlus imp )
+	{
+		return new VirtualStackImageLoader< ARGBType, VolatileARGBType, VolatileIntArray >(
+				imp, new VirtualStackVolatileARGBArrayLoader( imp ), new ARGBType(), new VolatileARGBType() )
+		{
+			@Override
+			protected void linkType( final CachedCellImg< ARGBType, VolatileIntArray > img )
+			{
+				img.setLinkedType( new ARGBType( img ) );
+			}
+
+			@Override
+			protected void linkVolatileType( final CachedCellImg< VolatileARGBType, VolatileIntArray > img )
+			{
+				img.setLinkedType( new VolatileARGBType( img ) );
+			}
+		};
+	}
+
 	private static double[][] mipmapResolutions = new double[][] { { 1, 1, 1 } };
 
 	private static AffineTransform3D[] mipmapTransforms = new AffineTransform3D[] { new AffineTransform3D() };
diff --git a/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java b/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..d586bdc76e81c30301de30b1054d8a2c28b58074
--- /dev/null
+++ b/src/main/java/bdv/img/virtualstack/VirtualStackVolatileARGBArrayLoader.java
@@ -0,0 +1,40 @@
+package bdv.img.virtualstack;
+
+import ij.ImagePlus;
+import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray;
+import bdv.img.cache.CacheArrayLoader;
+
+public class VirtualStackVolatileARGBArrayLoader implements CacheArrayLoader< VolatileIntArray >
+{
+	private final VolatileIntArray theEmptyArray;
+
+	private final ImagePlus imp;
+
+	public VirtualStackVolatileARGBArrayLoader( final ImagePlus imp )
+	{
+		theEmptyArray = new VolatileIntArray( imp.getWidth() * imp.getHeight(), false );
+		this.imp = imp;
+	}
+
+	@Override
+	public VolatileIntArray loadArray( final int timepoint, final int setup, final int level, final int[] dimensions, final long[] min ) throws InterruptedException
+	{
+		final int channel = setup + 1;
+		final int slice = ( int ) min[ 2 ] + 1;
+		final int frame = timepoint + 1;
+		final int[] data = ( int[] ) imp.getStack().getProcessor( imp.getStackIndex( channel, slice, frame ) ).getPixels();
+		return new VolatileIntArray( data, true );
+	}
+
+	@Override
+	public VolatileIntArray emptyArray( final int[] dimensions )
+	{
+		return theEmptyArray;
+	}
+
+	@Override
+	public int getBytesPerElement()
+	{
+		return 4;
+	}
+}