diff --git a/core/src/main/java/bdv/viewer/box/BdvBoundingBoxExample.java b/core/src/main/java/bdv/viewer/box/BdvBoundingBoxExample.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2e11268daeb8324fbd53e75db203a541ab438d1
--- /dev/null
+++ b/core/src/main/java/bdv/viewer/box/BdvBoundingBoxExample.java
@@ -0,0 +1,136 @@
+package bdv.viewer.box;
+
+import java.util.ArrayList;
+
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+
+import mpicbg.spim.data.SpimDataException;
+import mpicbg.spim.data.generic.sequence.AbstractSequenceDescription;
+import net.imglib2.Interval;
+import net.imglib2.util.Intervals;
+import bdv.BigDataViewer;
+import bdv.ViewerImgLoader;
+import bdv.spimdata.SpimDataMinimal;
+import bdv.spimdata.WrapBasicImgLoader;
+import bdv.spimdata.XmlIoSpimDataMinimal;
+import bdv.tools.InitializeViewerState;
+import bdv.tools.ToggleDialogAction;
+import bdv.tools.VisibilityAndGroupingDialog;
+import bdv.tools.brightness.BrightnessDialog;
+import bdv.tools.brightness.ConverterSetup;
+import bdv.tools.brightness.MinMaxGroup;
+import bdv.tools.brightness.RealARGBColorConverterSetup;
+import bdv.tools.brightness.SetupAssignments;
+import bdv.util.AbstractNamedAction.NamedActionAdder;
+import bdv.util.KeyProperties;
+import bdv.util.KeyProperties.KeyStrokeAdder;
+import bdv.viewer.DisplayMode;
+import bdv.viewer.NavigationActions;
+import bdv.viewer.SourceAndConverter;
+import bdv.viewer.ViewerFrame;
+import bdv.viewer.ViewerPanel;
+
+
+public class BdvBoundingBoxExample
+{
+	protected BdvBoundingBoxExample( final String xmlFilename ) throws SpimDataException
+	{
+		// =============== Load spimdata and create sources for display ==================
+
+		final SpimDataMinimal spimData = new XmlIoSpimDataMinimal().load( xmlFilename );
+		if ( WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ) )
+		{
+			System.err.println( "WARNING:\nOpening <SpimData> dataset that is not suited for suited for interactive browsing.\nConsider resaving as HDF5 for better performance." );
+		}
+		final AbstractSequenceDescription< ?, ?, ? > seq = spimData.getSequenceDescription();
+
+		final ArrayList< ConverterSetup > converterSetups = new ArrayList< ConverterSetup >();
+		final ArrayList< SourceAndConverter< ? > > sources = new ArrayList< SourceAndConverter< ? > >();
+		BigDataViewer.initSetups( spimData, converterSetups, sources );
+
+		final int width = 800;
+		final int height = 600;
+		final int numTimepoints = seq.getTimePoints().getTimePointsOrdered().size();
+		final ViewerFrame viewerFrame = new ViewerFrame( width, height, sources, numTimepoints,
+				( ( ViewerImgLoader< ?, ? > ) seq.getImgLoader() ).getCache() );
+		final ViewerPanel viewer = viewerFrame.getViewerPanel();
+
+
+		// =============== Create SetupAssignments, which encapsulate source color and brightness settings ==================
+
+		for ( final ConverterSetup cs : converterSetups )
+			if ( RealARGBColorConverterSetup.class.isInstance( cs ) )
+				( ( RealARGBColorConverterSetup ) cs ).setViewer( viewer );
+
+		final SetupAssignments setupAssignments = new SetupAssignments( converterSetups, 0, 65535 );
+		if ( setupAssignments.getMinMaxGroups().size() > 0 )
+		{
+			final MinMaxGroup group = setupAssignments.getMinMaxGroups().get( 0 );
+			for ( final ConverterSetup setup : setupAssignments.getConverterSetups() )
+				setupAssignments.moveSetupToGroup( setup, group );
+		}
+
+
+		// =============== Dialogs for brightness and visibility&grouping ==================
+
+		final BrightnessDialog brightnessDialog = new BrightnessDialog( viewerFrame, setupAssignments );
+		final VisibilityAndGroupingDialog activeSourcesDialog = new VisibilityAndGroupingDialog( viewerFrame, viewer.getVisibilityAndGrouping() );
+
+
+		// =============== the bounding box dialog ==================
+
+		final int boxSetupId = 9999; // some non-existing setup id
+		final Interval initialInterval = Intervals.createMinMax( 500, 100, -100, 1500, 800, 300 ); // the initially selected bounding box
+		final Interval rangeInterval = Intervals.createMinMax( -500, -500, -500, 3000, 3000, 1000 ); // the range (bounding box of possible bounding boxes)
+		final BoundingBoxDialog boundingBoxDialog = new BoundingBoxDialog( viewerFrame, viewer, setupAssignments, boxSetupId, initialInterval, rangeInterval );
+
+
+		// =============== install standard navigation shortcuts and S/F6/B for brightness/visibility&grouping/boundingbox dialogs ==================
+
+		final KeyProperties keyProperties = KeyProperties.readPropertyFile();
+		NavigationActions.installActionBindings( viewerFrame.getKeybindings(), viewer, keyProperties );
+		final String BRIGHTNESS_SETTINGS = "brightness settings";
+		final String VISIBILITY_AND_GROUPING = "visibility and grouping";
+		final String BOUNDING_BOX_DIALOG = "bounding box";
+		final ActionMap actionMap = new ActionMap();
+		final NamedActionAdder amap = new NamedActionAdder( actionMap );
+		amap.put( new ToggleDialogAction( BRIGHTNESS_SETTINGS, brightnessDialog ) );
+		amap.put( new ToggleDialogAction( VISIBILITY_AND_GROUPING, activeSourcesDialog ) );
+		amap.put( new ToggleDialogAction( BOUNDING_BOX_DIALOG, boundingBoxDialog ) );
+		final InputMap inputMap = new InputMap();
+		final KeyStrokeAdder imap = new KeyProperties( null ).adder( inputMap );
+		imap.put( BRIGHTNESS_SETTINGS, "S" );
+		imap.put( VISIBILITY_AND_GROUPING, "F6" );
+		imap.put( BOUNDING_BOX_DIALOG, "B" );
+		viewerFrame.getKeybindings().addActionMap( "bdvbox", actionMap );
+		viewerFrame.getKeybindings().addInputMap( "bdvbox", inputMap );
+
+		// =============== go to fused mode (overlay all sources) such that bounding box will be visible ==================
+		viewer.setDisplayMode( DisplayMode.FUSED );
+
+		// show viewer window
+		viewerFrame.setVisible( true );
+
+		// initialize transform and brightness
+		InitializeViewerState.initTransform( viewer );
+		InitializeViewerState.initBrightness( 0.001, 0.999, viewer, setupAssignments );
+
+//		( ( Hdf5ImageLoader ) seq.imgLoader ).initCachedDimensionsFromHdf5( false );
+	}
+
+	public static void main( final String[] args )
+	{
+		final String fn = "/Users/Pietzsch/Desktop/bdv example/drosophila 2.xml";
+		try
+		{
+			System.setProperty("apple.laf.useScreenMenuBar", "true");
+			new BdvBoundingBoxExample( fn );
+		}
+		catch ( final Exception e )
+		{
+			e.printStackTrace();
+		}
+	}
+
+}
diff --git a/core/src/main/java/bdv/viewer/box/BoundingBoxDialog.java b/core/src/main/java/bdv/viewer/box/BoundingBoxDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ee971db5633804db44b48e2b77f076e8f6aa00c
--- /dev/null
+++ b/core/src/main/java/bdv/viewer/box/BoundingBoxDialog.java
@@ -0,0 +1,143 @@
+package bdv.viewer.box;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.KeyStroke;
+
+import net.imglib2.Interval;
+import net.imglib2.display.RealARGBColorConverter;
+import net.imglib2.type.numeric.ARGBType;
+import net.imglib2.type.numeric.integer.UnsignedShortType;
+import bdv.tools.brightness.RealARGBColorConverterSetup;
+import bdv.tools.brightness.SetupAssignments;
+import bdv.viewer.SourceAndConverter;
+import bdv.viewer.ViewerPanel;
+
+// dialog to change bounding box
+// while dialog is visible, bounding box is added as a source to the viewer
+public class BoundingBoxDialog extends JDialog
+{
+	private final ViewerPanel viewer;
+
+	private final BoxRealRandomAccessible< UnsignedShortType > boxRealRandomAccessible;
+
+	private final BoxSelectionPanel boxSelectionPanel;
+
+	protected final SourceAndConverter< UnsignedShortType > boxSourceAndConverter;
+
+	protected final RealARGBColorConverterSetup boxConverterSetup;
+
+	public BoundingBoxDialog( final Frame owner, final ViewerPanel viewer, final SetupAssignments setupAssignments, final int boxSetupId, final Interval initialInterval, final Interval rangeInterval )
+	{
+		super( owner, "bounding box", false );
+		this.viewer = viewer;
+
+
+		// create a procedural RealRandomAccessible that will render the bounding box
+		final UnsignedShortType insideValue = new UnsignedShortType( 1000 ); // inside the box pixel value is 1000
+		final UnsignedShortType outsideValue = new UnsignedShortType( 0 ); // outside is 0
+		boxRealRandomAccessible = new BoxRealRandomAccessible< UnsignedShortType >( initialInterval, insideValue, outsideValue );
+
+		// create a bdv.viewer.Source providing data from the bbox RealRandomAccessible
+		final RealRandomAccessibleSource< UnsignedShortType > boxSource = new RealRandomAccessibleSource< UnsignedShortType >( boxRealRandomAccessible, "selection" )
+		{
+			@Override
+			public Interval getInterval( final int t, final int level )
+			{
+				return boxRealRandomAccessible.getInterval();
+			}
+		};
+
+		// set up a converter from the source type (UnsignedShortType in this case) to ARGBType
+		final RealARGBColorConverter< UnsignedShortType > converter = new RealARGBColorConverter.Imp1< UnsignedShortType >( 0, 3000 );
+		converter.setColor( new ARGBType( ARGBType.rgba( 0, 255, 0, 255 ) ) ); // set bounding box color to green
+
+		// create a ConverterSetup (can be used by the brightness dialog to adjust the converter settings)
+		boxConverterSetup = new RealARGBColorConverterSetup( boxSetupId, converter );
+		boxConverterSetup.setViewer( viewer );
+
+		// create a SourceAndConverter (can be added to the viewer for display
+		boxSourceAndConverter = new SourceAndConverter< UnsignedShortType >( boxSource, converter );
+
+
+		// create a JPanel with sliders to modify the bounding box interval (boxRealRandomAccessible.getInterval())
+		boxSelectionPanel = new BoxSelectionPanel( boxRealRandomAccessible.getInterval(), rangeInterval );
+		boxSelectionPanel.addSelectionUpdateListener( new BoxSelectionPanel.SelectionUpdateListener() // listen for updates on the bbox to trigger repainting
+		{
+			@Override
+			public void selectionUpdated()
+			{
+				viewer.requestRepaint();
+			}
+		} );
+
+
+		// button prints the bounding box interval
+		final JButton button = new JButton( "ok" );
+		button.addActionListener( new AbstractAction()
+		{
+			@Override
+			public void actionPerformed( final ActionEvent e )
+			{
+				System.out.println( "bounding box:" + net.imglib2.util.Util.printInterval( boxRealRandomAccessible.getInterval() ) );
+			}
+		} );
+
+
+		// when dialog is made visible, add bbox source
+		// when dialog is hidden, remove bbox source
+		addComponentListener( new ComponentAdapter()
+		{
+			@Override
+			public void componentShown( final ComponentEvent e )
+			{
+				viewer.addSource( boxSourceAndConverter );
+				setupAssignments.addSetup( boxConverterSetup );
+			}
+
+			@Override
+			public void componentHidden( final ComponentEvent e )
+			{
+				viewer.removeSource( boxSourceAndConverter.getSpimSource() );
+				setupAssignments.removeSetup( boxConverterSetup );
+			}
+		} );
+
+
+		// make ESC key hide dialog
+		final ActionMap am = getRootPane().getActionMap();
+		final InputMap im = getRootPane().getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
+		final Object hideKey = new Object();
+		final Action hideAction = new AbstractAction()
+		{
+			@Override
+			public void actionPerformed( final ActionEvent e )
+			{
+				setVisible( false );
+			}
+		};
+		im.put( KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), hideKey );
+		am.put( hideKey, hideAction );
+
+
+		// layout and show dialog
+		final Container content = getContentPane();
+		content.add( boxSelectionPanel, BorderLayout.NORTH );
+		content.add( button, BorderLayout.SOUTH );
+		pack();
+		setDefaultCloseOperation( JDialog.HIDE_ON_CLOSE );
+	}
+}
diff --git a/core/src/main/java/bdv/viewer/box/BoxRealRandomAccessible.java b/core/src/main/java/bdv/viewer/box/BoxRealRandomAccessible.java
new file mode 100644
index 0000000000000000000000000000000000000000..23de2e29f404ba0878a2da4aaa9ab8921217c27c
--- /dev/null
+++ b/core/src/main/java/bdv/viewer/box/BoxRealRandomAccessible.java
@@ -0,0 +1,84 @@
+package bdv.viewer.box;
+
+import net.imglib2.Interval;
+import net.imglib2.RealInterval;
+import net.imglib2.RealPoint;
+import net.imglib2.RealRandomAccess;
+import net.imglib2.RealRandomAccessible;
+import net.imglib2.type.Type;
+import net.imglib2.util.Intervals;
+import bdv.util.ModifiableInterval;
+
+// a simple imglib2 RealRandomAccessible that has one value inside a box and another value outside
+public class BoxRealRandomAccessible< T extends Type< T > > implements RealRandomAccessible< T >
+{
+	private final int n;
+
+	private final ModifiableInterval interval;
+
+	private final T insideValue;
+
+	private final T outsideValue;
+
+	public BoxRealRandomAccessible( final Interval interval, final T insideValue, final T outsideValue )
+	{
+		n = interval.numDimensions();
+		this.interval = new ModifiableInterval( interval );
+		this.insideValue = insideValue.copy();
+		this.outsideValue = outsideValue.copy();
+	}
+
+	@Override
+	public int numDimensions()
+	{
+		return n;
+	}
+
+	public class Access extends RealPoint implements RealRandomAccess< T >
+	{
+		public Access()
+		{
+			super( BoxRealRandomAccessible.this.n );
+		}
+
+		protected Access( final Access a )
+		{
+			super( a );
+		}
+
+		@Override
+		public T get()
+		{
+			return Intervals.contains( interval, this ) ? insideValue : outsideValue;
+		}
+
+		@Override
+		public Access copy()
+		{
+			return new Access( this );
+		}
+
+		@Override
+		public Access copyRealRandomAccess()
+		{
+			return copy();
+		}
+	}
+
+	@Override
+	public RealRandomAccess< T > realRandomAccess()
+	{
+		return new Access();
+	}
+
+	@Override
+	public RealRandomAccess< T > realRandomAccess( final RealInterval interval )
+	{
+		return new Access();
+	}
+
+	public ModifiableInterval getInterval()
+	{
+		return interval;
+	}
+}
diff --git a/core/src/main/java/bdv/viewer/box/BoxSelectionPanel.java b/core/src/main/java/bdv/viewer/box/BoxSelectionPanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..f88c8c8e34cf1bb9d3518aa4b21d99c333941567
--- /dev/null
+++ b/core/src/main/java/bdv/viewer/box/BoxSelectionPanel.java
@@ -0,0 +1,108 @@
+package bdv.viewer.box;
+
+import java.util.ArrayList;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+import net.imglib2.FinalInterval;
+import net.imglib2.Interval;
+import bdv.tools.brightness.SliderPanel;
+import bdv.util.BoundedInterval;
+import bdv.util.ModifiableInterval;
+
+// a JPanel containing X,Y,Z min/max sliders for adjusting an interval
+public class BoxSelectionPanel extends JPanel
+{
+	public static interface SelectionUpdateListener
+	{
+		public void selectionUpdated();
+	}
+
+	private static final long serialVersionUID = 1L;
+
+	private final BoundedInterval[] ranges;
+
+	private final ModifiableInterval selection;
+
+	private final ArrayList< SelectionUpdateListener > listeners;
+
+	public BoxSelectionPanel( final ModifiableInterval selection, final Interval rangeInterval )
+	{
+		final int n = selection.numDimensions();
+		this.selection = selection;
+		ranges = new BoundedInterval[ n ];
+		listeners = new ArrayList< SelectionUpdateListener >();
+
+		setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
+		for ( int d = 0; d < n; ++d )
+		{
+			final int rangeMin = ( int ) rangeInterval.min( d );
+			final int rangeMax = ( int ) rangeInterval.max( d );
+			final int initialMin = Math.max( ( int ) selection.min( d ), rangeMin );
+			final int initialMax = Math.min( ( int ) selection.max( d ), rangeMax );
+			final BoundedInterval range = new BoundedInterval( rangeMin, rangeMax, initialMin, initialMax, 1 )
+			{
+				@Override
+				protected void updateInterval( final int min, final int max )
+				{
+					updateSelection();
+				}
+			};
+			final JPanel sliders = new JPanel();
+			sliders.setLayout( new BoxLayout( sliders, BoxLayout.PAGE_AXIS ) );
+			final SliderPanel minPanel = new SliderPanel( "min", range.getMinBoundedValue(), 1 );
+			minPanel.setBorder( BorderFactory.createEmptyBorder( 0, 10, 10, 10 ) );
+			sliders.add( minPanel );
+			final SliderPanel maxPanel = new SliderPanel( "max", range.getMaxBoundedValue(), 1 );
+			maxPanel.setBorder( BorderFactory.createEmptyBorder( 0, 10, 10, 10 ) );
+			sliders.add( maxPanel );
+			add( sliders );
+			ranges[ d ] = range;
+		}
+	}
+
+	public void setBoundsInterval( final Interval interval )
+	{
+		final int n = selection.numDimensions();
+		for ( int d = 0; d < n; ++d )
+			ranges[ d ].setRange( ( int ) interval.min( d ), ( int ) interval.max( d ) );
+	}
+
+	public void addSelectionUpdateListener( final SelectionUpdateListener l )
+	{
+		listeners.add( l );
+	}
+
+	public void updateSelection()
+	{
+		final int n = selection.numDimensions();
+		final long[] min = new long[ n ];
+		final long[] max = new long[ n ];
+		for ( int d = 0; d < n; ++d )
+		{
+			min[ d ] = ranges[ d ].getMinBoundedValue().getCurrentValue();
+			max[ d ] = ranges[ d ].getMaxBoundedValue().getCurrentValue();
+		}
+		selection.set( new FinalInterval( min, max ) );
+		for ( final SelectionUpdateListener l : listeners )
+			l.selectionUpdated();
+	}
+
+	public void updateSliders( final Interval interval )
+	{
+		final int n = selection.numDimensions();
+		if ( interval.numDimensions() != n )
+			throw new IllegalArgumentException();
+		final long[] min = new long[ n ];
+		final long[] max = new long[ n ];
+		interval.min( min );
+		interval.max( max );
+		for ( int d = 0; d < n; ++d )
+		{
+			ranges[ d ].getMinBoundedValue().setCurrentValue( ( int ) min[ d ] );
+			ranges[ d ].getMaxBoundedValue().setCurrentValue( ( int ) max[ d ] );
+		}
+	}
+}
diff --git a/core/src/main/java/bdv/viewer/box/RealRandomAccessibleSource.java b/core/src/main/java/bdv/viewer/box/RealRandomAccessibleSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f06af37332c8635a2017df9ecbecb87411bb5ec
--- /dev/null
+++ b/core/src/main/java/bdv/viewer/box/RealRandomAccessibleSource.java
@@ -0,0 +1,78 @@
+package bdv.viewer.box;
+
+import net.imglib2.Interval;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealRandomAccessible;
+import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.type.Type;
+import net.imglib2.util.Util;
+import net.imglib2.view.Views;
+import bdv.viewer.Interpolation;
+import bdv.viewer.Source;
+
+/**
+ * A {@link Source} wrapping some {@link RealRandomAccessible}.
+ *
+ * @param <T>
+ *
+ * @author Tobias Pietzsch <tobias.pietzsch@gmail.com>
+ */
+public abstract class RealRandomAccessibleSource< T extends Type< T > > implements Source< T >
+{
+	protected RealRandomAccessible< T > accessible;
+
+	protected final T type;
+
+	protected final String name;
+
+	public RealRandomAccessibleSource( final RealRandomAccessible< T > accessible, final String name )
+	{
+		this.accessible = accessible;
+		this.type = Util.getTypeFromRealRandomAccess( accessible ).createVariable();
+		this.name = name;
+	}
+
+	@Override
+	public boolean isPresent( final int t )
+	{
+		return true;
+	}
+
+	public abstract Interval getInterval( final int t, final int level );
+
+	@Override
+	public RandomAccessibleInterval< T > getSource( final int t, final int level )
+	{
+		return Views.interval( Views.raster( accessible ), getInterval( t, level ) );
+	}
+
+	@Override
+	public RealRandomAccessible< T > getInterpolatedSource( final int t, final int level, final Interpolation method )
+	{
+		return accessible;
+	}
+
+	@Override
+	public AffineTransform3D getSourceTransform( final int t, final int level )
+	{
+		return new AffineTransform3D();
+	}
+
+	@Override
+	public T getType()
+	{
+		return type;
+	}
+
+	@Override
+	public String getName()
+	{
+		return name;
+	}
+
+	@Override
+	public int getNumMipmapLevels()
+	{
+		return 1;
+	}
+}