diff --git a/src/main/java/bdv/BigDataViewer.java b/src/main/java/bdv/BigDataViewer.java
index 9c86ff48d0779cb8f8ca250638896d56feed89bd..efa9b082bb4f8ff53c6fc5ce4a13c38593406415 100644
--- a/src/main/java/bdv/BigDataViewer.java
+++ b/src/main/java/bdv/BigDataViewer.java
@@ -380,8 +380,10 @@ public class BigDataViewer
 	public static void main( final String[] args )
 	{
 //		final String fn = "/Users/pietzsch/desktop/data/catmaid-confocal.xml";
-		final String fn = "/home/saalfeld/openconnectome-bock11-neariso.xml";
+//		final String fn = "/home/saalfeld/openconnectome-bock11-neariso.xml";
 //		final String fn = "/home/saalfeld/catmaid-confocal.xml";
+//		final String fn = "/home/saalfeld/trakem2-test.xml";
+		final String fn = "/home/saalfeld/trakem2-fly-test.xml";
 //		final String fn = "/Users/pietzsch/desktop/data/BDV130418A325/BDV130418A325_NoTempReg.xml";
 //		final String fn = "/Users/pietzsch/Desktop/data/valia2/valia.xml";
 //		final String fn = "/Users/pietzsch/workspace/data/fast fly/111010_weber/combined.xml";
diff --git a/src/main/java/bdv/img/trakem2/TrakEM2ImageLoader.java b/src/main/java/bdv/img/trakem2/TrakEM2ImageLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc3978fe1ab429659ba6ca751d1cdcfdc298c3e7
--- /dev/null
+++ b/src/main/java/bdv/img/trakem2/TrakEM2ImageLoader.java
@@ -0,0 +1,182 @@
+package bdv.img.trakem2;
+
+import ij.measure.Calibration;
+import ini.trakem2.ControlWindow;
+import ini.trakem2.Project;
+import ini.trakem2.display.LayerSet;
+import ini.trakem2.persistence.Loader;
+
+import java.awt.Rectangle;
+import java.io.File;
+
+import mpicbg.spim.data.View;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.img.NativeImg;
+import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray;
+import net.imglib2.img.cell.CellImg;
+import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.numeric.ARGBType;
+import net.imglib2.type.volatiles.VolatileARGBType;
+
+import org.jdom2.Element;
+
+import bdv.AbstractViewerImgLoader;
+import bdv.img.cache.VolatileCell;
+import bdv.img.cache.VolatileGlobalCellCache;
+import bdv.img.cache.VolatileGlobalCellCache.LoadingStrategy;
+import bdv.img.cache.VolatileImgCells;
+import bdv.img.cache.VolatileImgCells.CellCache;
+
+public class TrakEM2ImageLoader extends AbstractViewerImgLoader< ARGBType, VolatileARGBType >
+{
+	private Project project;
+	private Loader loader;
+	private LayerSet layerset;
+	
+	private int width;
+	private int height;
+	private int depth;
+	
+	private double zScale;
+
+	private int tileWidth;
+	private int tileHeight;
+
+	private int numScales;
+
+	private double[][] mipmapResolutions;
+
+	private AffineTransform3D[] mipmapTransforms;
+
+	private long[][] imageDimensions;
+
+	private int[][] blockDimensions;
+
+	private VolatileGlobalCellCache< VolatileIntArray > cache;
+
+	public TrakEM2ImageLoader()
+	{
+		super( new ARGBType(), new VolatileARGBType() );
+	}
+
+	@Override
+	public void init( final Element elem, final File basePath )
+	{
+		final String projectPath = elem.getChildText( "projectPath" );
+		
+		ControlWindow.setGUIEnabled( false );
+		project = Project.openFSProject( projectPath, false);
+		loader = project.getLoader();
+		layerset = project.getRootLayerSet();
+		layerset.setSnapshotsMode(1);
+		final Rectangle box = layerset.get2DBounds();
+		
+		width = box.width;
+		height = box.height;
+		depth = layerset.getLayers().size();
+
+		final Calibration calibration = layerset.getCalibration();
+		zScale = calibration.pixelDepth / calibration.pixelWidth;
+		
+		tileWidth = Integer.parseInt( elem.getChildText( "tileWidth" ) );
+		tileHeight = Integer.parseInt( elem.getChildText( "tileHeight" ) );
+
+		final String numScalesString = elem.getChildText( "numScales" );
+		if ( numScalesString == null )
+			numScales = getNumScales( width, height, tileWidth, tileHeight );
+		else
+			numScales = Integer.parseInt( numScalesString );
+
+		mipmapResolutions = new double[ numScales ][];
+		imageDimensions = new long[ numScales ][];
+		blockDimensions = new int[ numScales ][];
+		mipmapTransforms = new AffineTransform3D[ numScales ];
+		for ( int l = 0; l < numScales; ++l )
+		{
+			mipmapResolutions[ l ] = new double[] { 1 << l, 1 << l, 1 };
+			imageDimensions[ l ] = new long[] { width >> l, height >> l, depth };
+			blockDimensions[ l ] = new int[] { tileWidth, tileHeight, 1 };
+			
+			final AffineTransform3D mipmapTransform = new AffineTransform3D();
+			mipmapTransform.set( mipmapResolutions[ l ][ 0 ], 0, 0 );
+			mipmapTransform.set( mipmapResolutions[ l ][ 1 ], 1, 1 );
+			mipmapTransform.set( zScale, 2, 2 );
+			mipmapTransform.set( 0.5 * ( mipmapResolutions[ l ][ 0 ] - 1 ), 0, 3 );
+			mipmapTransform.set( 0.5 * ( mipmapResolutions[ l ][ 0 ] - 1 ), 1, 3 );
+			mipmapTransforms[ l ] = mipmapTransform;
+		}
+
+		final int[] maxLevels = new int[] { numScales - 1 };
+		cache = new VolatileGlobalCellCache< VolatileIntArray >(
+				new TrakEM2VolatileIntArrayLoader( loader, layerset ), 1, 1, numScales, maxLevels, 10 );
+	}
+
+	final static public int getNumScales( long width, long height, final long tileWidth, final long tileHeight )
+	{
+		int i = 1;
+
+		while ( ( width >>= 1 ) > tileWidth && ( height >>= 1 ) > tileHeight )
+			++i;
+
+		return i;
+	}
+
+	@Override
+	public RandomAccessibleInterval< ARGBType > getImage( final View view, final int level )
+	{
+		final CellImg< ARGBType, VolatileIntArray, VolatileCell< VolatileIntArray > >  img = prepareCachedImage( view, level, LoadingStrategy.BLOCKING );
+		final ARGBType linkedType = new ARGBType( img );
+		img.setLinkedType( linkedType );
+		return img;
+	}
+
+	@Override
+	public RandomAccessibleInterval< VolatileARGBType > getVolatileImage( final View view, final int level )
+	{
+		final CellImg< VolatileARGBType, VolatileIntArray, VolatileCell< VolatileIntArray > >  img = prepareCachedImage( view, level, LoadingStrategy.VOLATILE );
+		final VolatileARGBType linkedType = new VolatileARGBType( img );
+		img.setLinkedType( linkedType );
+		return img;
+	}
+
+	@Override
+	public double[][] getMipmapResolutions( final int setup )
+	{
+		return mipmapResolutions;
+	}
+
+	@Override
+	public int numMipmapLevels( final int setup )
+	{
+		return numScales;
+	}
+
+	/**
+	 * (Almost) create a {@link CellImg} backed by the cache.
+	 * The created image needs a {@link NativeImg#setLinkedType(net.imglib2.type.Type) linked type} before it can be used.
+	 * The type should be either {@link ARGBType} and {@link VolatileARGBType}.
+	 */
+	protected < T extends NativeType< T > > CellImg< T, VolatileIntArray, VolatileCell< VolatileIntArray > > prepareCachedImage( final View view, final int level, final LoadingStrategy loadingStrategy )
+	{
+		final long[] dimensions = imageDimensions[ level ];
+		final int[] cellDimensions = blockDimensions[ level ];
+
+		final CellCache< VolatileIntArray > c = cache.new VolatileCellCache( view.getTimepointIndex(), view.getSetupIndex(), level, loadingStrategy );
+		final VolatileImgCells< VolatileIntArray > cells = new VolatileImgCells< VolatileIntArray >( c, 1, dimensions, cellDimensions );
+		final CellImg< T, VolatileIntArray, VolatileCell< VolatileIntArray > > img = new CellImg< T, VolatileIntArray, VolatileCell< VolatileIntArray > >( null, cells );
+		return img;
+	}
+
+	@Override
+	public VolatileGlobalCellCache< VolatileIntArray > getCache()
+	{
+		return cache;
+	}
+
+	@Override
+	public AffineTransform3D[] getMipmapTransforms( final int setup )
+	{
+		return mipmapTransforms;
+	}
+}
diff --git a/src/main/java/bdv/img/trakem2/TrakEM2VolatileIntArrayLoader.java b/src/main/java/bdv/img/trakem2/TrakEM2VolatileIntArrayLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..411596aaf6f6cf74419633fa8a82850fe2ee8ac0
--- /dev/null
+++ b/src/main/java/bdv/img/trakem2/TrakEM2VolatileIntArrayLoader.java
@@ -0,0 +1,93 @@
+package bdv.img.trakem2;
+
+import ij.ImagePlus;
+import ini.trakem2.display.LayerSet;
+import ini.trakem2.display.Patch;
+import ini.trakem2.persistence.Loader;
+
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.image.PixelGrabber;
+
+import net.imglib2.img.basictypeaccess.volatiles.array.VolatileIntArray;
+import bdv.img.cache.CacheArrayLoader;
+
+public class TrakEM2VolatileIntArrayLoader implements CacheArrayLoader< VolatileIntArray >
+{
+	private VolatileIntArray theEmptyArray;
+
+	private final Loader loader;
+	
+	private final LayerSet layerset;
+	
+	/**
+	 * <p>Create a {@link CacheArrayLoader} for a TrakEM2 source.  Tiles are
+	 * addressed by their pixel position and dimension.</p>
+	 *  
+	 * @param 
+	 * @param tileWidth
+	 * @param tileHeight
+	 */
+	public TrakEM2VolatileIntArrayLoader( final Loader loader, final LayerSet layerset )
+	{
+		theEmptyArray = new VolatileIntArray(1, false );
+		this.loader = loader;
+		this.layerset = layerset;
+	}
+	
+	@Override
+	public int getBytesPerElement()
+	{
+		return 4;
+	}
+
+	@Override
+	public VolatileIntArray loadArray(
+			final int timepoint,
+			final int setup,
+			final int level,
+			final int[] dimensions,
+			final long[] min ) throws InterruptedException
+	{
+		final int iScale = 1 << level;
+		final double scale = 1.0 / iScale;
+		final Rectangle box = new Rectangle( ( int )( min[ 0 ] * iScale ), ( int )( min[ 1 ] * iScale ), dimensions[ 0 ] * iScale, dimensions[ 1 ] * iScale );
+		
+		final int[] data = new int[ dimensions[ 0 ] * dimensions[ 1 ] ];
+		try
+		{
+			final Image image = loader.getFlatAWTImage(
+					layerset.getLayer( ( int )min[ 2 ] ),
+					box,
+					scale,
+					0xffffffff,
+					ImagePlus.COLOR_RGB,
+					Patch.class,
+					null,
+					true,
+					new Color( 0x00000000, true ) );
+			final PixelGrabber pg = new PixelGrabber( image, 0, 0, dimensions[ 0 ], dimensions[ 1 ], data, 0, dimensions[ 0 ] );
+			pg.grabPixels();
+
+//			System.out.println( "success loading r=" + entry.key.r + " c=" + entry.key.c + " url(" + urlString + ")" );
+
+		}
+		catch ( final Exception e )
+		{
+			e.printStackTrace();
+		}
+		return new VolatileIntArray( data, true );
+	}
+
+	@Override
+	public VolatileIntArray emptyArray( final int[] dimensions )
+	{
+		int numEntities = 1;
+		for ( int i = 0; i < dimensions.length; ++i )
+			numEntities *= dimensions[ i ];
+		if ( theEmptyArray.getCurrentStorageArray().length < numEntities )
+			theEmptyArray = new VolatileIntArray( numEntities, false );
+		return theEmptyArray;
+	}
+}