diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/FXFrame.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/FXFrame.java
index a91f5d56ba221c608db128d1f5b26152eeb2fc17..47b16deb83535b89a80646e55dc80e984bf6884d 100644
--- a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/FXFrame.java
+++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/FXFrame.java
@@ -2,12 +2,14 @@ package cz.it4i.fiji.haas.ui;
 
 import java.awt.BorderLayout;
 import java.awt.Dimension;
-import java.awt.Frame;
 import java.awt.Window;
 import java.awt.im.InputMethodRequests;
 import java.io.IOException;
 import java.net.URL;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 import javax.swing.JDialog;
 import javax.swing.JScrollPane;
@@ -16,10 +18,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javafx.application.Platform;
+import javafx.beans.value.ObservableValue;
 import javafx.embed.swing.JFXPanel;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
 
 public class FXFrame<C extends FXFrame.Controller> extends JDialog {
 
@@ -36,6 +41,25 @@ public class FXFrame<C extends FXFrame.Controller> extends JDialog {
 
 	public interface Controller {
 		void init(Window frame);
+		static public <V> void executeAsync(Executor executor, Callable<V> action, Consumer<V> postAction) {
+			executor.execute(() -> {
+				V result;
+				try {
+					result = action.call();
+					postAction.accept(result);
+				} catch (Exception e) {
+					log.error(e.getMessage(), e);
+				}
+				
+			});
+		}
+		
+		@SuppressWarnings("unchecked")
+		static public <U,T extends ObservableValue<U>> void setCellValueFactory(TableView<T> tableView,int index, Function<U, String> mapper) {
+			((TableColumn<T, String>) tableView.getColumns().get(index))
+					.setCellValueFactory(f -> new ObservableValueAdapter<U, String>(f.getValue(), mapper));
+
+		}
 	}
 
 	private static final long serialVersionUID = 1L;
@@ -48,8 +72,8 @@ public class FXFrame<C extends FXFrame.Controller> extends JDialog {
 		this(null, fxmlFile);
 	}
 
-	public FXFrame(Frame applicationFrame, String string) {
-		super(applicationFrame);
+	public FXFrame(Window applicationFrame, String string) {
+		super(applicationFrame,ModalityType.MODELESS);
 		fxmlFile = string;
 	}
 
diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/ObservableValueRegistry.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/ObservableValueRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..d210a4f4575f278890277e47b89868ee46f0515a
--- /dev/null
+++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/ObservableValueRegistry.java
@@ -0,0 +1,44 @@
+package cz.it4i.fiji.haas.ui;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javafx.beans.value.ObservableValue;
+
+public class ObservableValueRegistry<T> {
+
+	private Function<T, Boolean> updateFunction;
+	private Function<T, Boolean> validateFunction;
+	private Consumer<T> removeConsumer;
+	
+	
+	public ObservableValueRegistry(Function<T, Boolean> validateFunction, Function<T, Boolean> updateFunction,
+			Consumer<T> removeConsumer) {
+		super();
+		this.validateFunction = validateFunction;
+		this.updateFunction = updateFunction;
+		this.removeConsumer = removeConsumer;
+	}
+
+	private Map<T,UpdatableObservableValue<T>> map = new HashMap<>(); 
+	
+	public  ObservableValue<T> addIfAbsent(T value) {
+		UpdatableObservableValue<T> uov = map.computeIfAbsent(value, v-> new UpdatableObservableValue<T>(v, updateFunction, validateFunction));
+		return uov;
+	}
+	
+	public ObservableValue<T> remove(T value) {
+		return map.get(value);
+	}
+	
+	public void update() {
+		for (UpdatableObservableValue<T> value : new LinkedList<>(map.values())) {
+			if(!value.update()) {
+				removeConsumer.accept(value.getValue());
+			}
+		}
+	}
+}
diff --git a/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/UpdatableObservableValue.java b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/UpdatableObservableValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bd8930da1e6dbe718c8cadeb8bb106210ab947c
--- /dev/null
+++ b/haas-imagej-client/src/main/java/cz/it4i/fiji/haas/ui/UpdatableObservableValue.java
@@ -0,0 +1,37 @@
+package cz.it4i.fiji.haas.ui;
+
+import java.util.function.Function;
+
+import javafx.beans.value.ObservableValueBase;
+
+public class UpdatableObservableValue<T> extends ObservableValueBase<T>{
+
+	private T wrapped;
+	private Function<T,Boolean> updateFunction;
+	private Function<T,Boolean> validateFunction;
+	
+	
+	public UpdatableObservableValue(T wrapped, Function<T, Boolean> updateFunction, Function<T, Boolean> validateFunction) {
+		super();
+		this.wrapped = wrapped;
+		this.updateFunction = updateFunction;
+		this.validateFunction = validateFunction;
+	}
+
+	@Override
+	public T getValue() {
+		
+		return wrapped;
+	}
+
+	public boolean update() {
+		if(!validateFunction.apply(wrapped)) {
+			return false;
+		}
+		if(updateFunction.apply(wrapped)) {
+			fireValueChangedEvent();
+		}
+		return true;
+	}
+	
+}
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/Task.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/Task.java
index 84af6203020fe9a90b10200c869d33eb2ee07516..285a579ff2e7d0d91dbdebb594485e0ece3f3965 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/Task.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/Task.java
@@ -1,12 +1,12 @@
 package cz.it4i.fiji.haas_spim_benchmark.core;
 
-import java.util.Collection;
 import java.util.LinkedList;
+import java.util.List;
 
 public class Task {
 	private SPIMComputationAccessor outputHolder;
 	private String description;
-	private Collection<TaskComputation> computations;
+	private List<TaskComputation> computations;
 	private int numComputations;
 
 	public Task(SPIMComputationAccessor outputHolder, String description, int numComputations) {
@@ -15,7 +15,7 @@ public class Task {
 		this.numComputations = numComputations;
 	}
 
-	public Collection<TaskComputation> getComputations() {
+	public List<TaskComputation> getComputations() {
 		if (computations == null) {
 			fillComputations();
 		}
@@ -33,4 +33,9 @@ public class Task {
 		}
 	}
 
+	public void update() {
+		// TODO Auto-generated method stub
+		
+	}
+
 }
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/TaskComputation.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/TaskComputation.java
index 2d1c7b221d415bac344dbd9221a164108eac5ed7..c0fc812c89d4e0601ec441af204ecedc53458156 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/TaskComputation.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/core/TaskComputation.java
@@ -29,6 +29,10 @@ public class TaskComputation {
 		updateState();//TASK 1011 it is not good idea update every time when state is requested 
 		return state != null?state:JobState.Configuring;
 	}
+	
+	public int getTimepoint() {
+		return timepoint;
+	}
 
 	private void updateState() {
 		String snakeOutput = outputHolder.getActualOutput();
@@ -65,5 +69,10 @@ public class TaskComputation {
 	//
 	//or return
 	}
+
+	public void update() {
+		// TODO Auto-generated method stub
+		
+	}
 	
 }
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMController.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMController.java
index 74097b097dfb42d8defe6599a6cbef0b3b24d6bb..cd35978fd422aa0ba1ec822de5d6d31faae4e110 100644
--- a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMController.java
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/BenchmarkSPIMController.java
@@ -15,7 +15,6 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -28,7 +27,6 @@ import org.slf4j.LoggerFactory;
 import cz.it4i.fiji.haas.ui.DummyProgress;
 import cz.it4i.fiji.haas.ui.FXFrame;
 import cz.it4i.fiji.haas.ui.ModalDialogs;
-import cz.it4i.fiji.haas.ui.ObservableValueAdapter;
 import cz.it4i.fiji.haas.ui.ProgressDialog;
 import cz.it4i.fiji.haas.ui.TableViewContextMenu;
 import cz.it4i.fiji.haas_java_client.JobState;
@@ -37,7 +35,6 @@ import cz.it4i.fiji.haas_spim_benchmark.core.BenchmarkJobManager.BenchmarkJob;
 import cz.it4i.fiji.haas_spim_benchmark.core.Constants;
 import cz.it4i.fiji.haas_spim_benchmark.core.FXFrameExecutorService;
 import javafx.fxml.FXML;
-import javafx.scene.control.TableColumn;
 import javafx.scene.control.TableView;
 import net.imagej.updater.util.Progress;
 
@@ -98,11 +95,21 @@ public class BenchmarkSPIMController implements FXFrame.Controller {
 
 	private void initMenu() {
 		TableViewContextMenu<BenchmarkJob> menu = new TableViewContextMenu<>(jobs);
-		menu.addItem("Create job", x -> executeWSCallAsync("Creating job", p -> manager.createJob()),
-				j -> true);
+		menu.addItem("Create job", x -> executeWSCallAsync("Creating job", p -> manager.createJob()), j -> true);
 		menu.addItem("Start job", job -> executeWSCallAsync("Starting job", p -> job.startJob(p)),
 				job -> notNullValue(job,
 						j -> j.getState() == JobState.Configuring || j.getState() == JobState.Finished));
+
+		menu.addItem("Show progress", job -> {
+			try {
+				new SPIMPipelineProgressViewWindow(root, job).setVisible(true);
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				log.error(e.getMessage(), e);
+			}
+		}, job -> notNullValue(job, j -> j.getState() == JobState.Running || j.getState() == JobState.Finished
+				|| j.getState() == JobState.Failed));
+
 		menu.addItem("Download result", job -> executeWSCallAsync("Downloading data", p -> job.downloadData(p)),
 				job -> notNullValue(job,
 						j -> EnumSet.of(JobState.Failed, JobState.Finished).contains(j.getState()) && !j.downloaded()));
@@ -129,31 +136,21 @@ public class BenchmarkSPIMController implements FXFrame.Controller {
 		});
 	}
 
-	private <V> void executeAsync(Executor executor, Callable<V> action, Consumer<V> postAction) {
-		executor.execute(() -> {
-			V result;
-			try {
-				result = action.call();
-				postAction.accept(result);
-			} catch (Exception e) {
-				log.error(e.getMessage(), e);
-			}
-			
-		});
-	}
-
 	private void executeWSCallAsync(String title, P_JobAction action) {
 		executeWSCallAsync(title, true, action);
 	}
 
 	private void executeWSCallAsync(String title, boolean update, P_JobAction action) {
-		executeAsync(executorServiceWS, (Callable<Void>) ()->{
+		FXFrame.Controller.executeAsync(executorServiceWS, (Callable<Void>) () -> {
 			ProgressDialog dialog = ModalDialogs.doModal(new ProgressDialog(root, title),
 					WindowConstants.DO_NOTHING_ON_CLOSE);
 			action.doAction(dialog);
 			dialog.done();
 			return null;
-		}, x-> {if(update)  updateJobs(); });
+		}, x -> {
+			if (update)
+				updateJobs();
+		});
 	}
 
 	private void updateJobs() {
@@ -172,13 +169,11 @@ public class BenchmarkSPIMController implements FXFrame.Controller {
 				throw new RuntimeException(e1);
 			}
 			executorServiceUI.execute(() -> {
-				
 
 				Set<BenchmarkJob> old = new HashSet<BenchmarkJob>(jobs.getItems());
 				Map<BenchmarkJob, BenchmarkJob> actual;
 				try {
-					actual = manager.getJobs().stream().
-							collect(Collectors.toMap(job -> job, job -> job));
+					actual = manager.getJobs().stream().collect(Collectors.toMap(job -> job, job -> job));
 				} catch (IOException e) {
 					throw new RuntimeException(e);
 				}
@@ -199,7 +194,7 @@ public class BenchmarkSPIMController implements FXFrame.Controller {
 				});
 			});
 		});
-		
+
 	}
 
 	private void initTable() {
@@ -210,11 +205,8 @@ public class BenchmarkSPIMController implements FXFrame.Controller {
 		setCellValueFactory(4, j -> j.getEndTime().toString());
 	}
 
-	@SuppressWarnings("unchecked")
 	private void setCellValueFactory(int index, Function<BenchmarkJob, String> mapper) {
-		((TableColumn<BenchmarkJob, String>) jobs.getColumns().get(index))
-				.setCellValueFactory(f -> new ObservableValueAdapter<BenchmarkJob, String>(f.getValue(), mapper));
-
+		FXFrame.Controller.setCellValueFactory(jobs, index, mapper);
 	}
 
 	private interface P_JobAction {
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/ObservableTaskRegistry.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/ObservableTaskRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..71876913b7daa56044916f17b844330e6f216967
--- /dev/null
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/ObservableTaskRegistry.java
@@ -0,0 +1,30 @@
+package cz.it4i.fiji.haas_spim_benchmark.ui;
+
+import java.util.function.Consumer;
+
+import cz.it4i.fiji.haas.ui.ObservableValueRegistry;
+import cz.it4i.fiji.haas_java_client.JobState;
+import cz.it4i.fiji.haas_spim_benchmark.core.Task;
+import cz.it4i.fiji.haas_spim_benchmark.core.TaskComputation;
+
+public class ObservableTaskRegistry extends ObservableValueRegistry<Task>{
+
+	public ObservableTaskRegistry(
+			Consumer<Task> removeConsumer) {
+		super(x->true, t->update(t), removeConsumer);
+	}
+
+	private static boolean update(Task t) {
+		boolean result = true;
+		t.update();
+		for(TaskComputation tc: t.getComputations()) {
+			JobState oldState = tc.getState();
+			tc.update();
+			result &= oldState == tc.getState();
+		}
+		return result;
+	}
+
+	
+
+}
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressView.fxml b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressView.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..f16482fd67db7aced47a66a7f25f68f2e57bd351
--- /dev/null
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressView.fxml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.TableColumn?>
+<?import javafx.scene.control.TableView?>
+<?import javafx.scene.layout.BorderPane?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="912.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="cz.it4i.fiji.haas_spim_benchmark.ui.SPIMPipelineProgressViewController">
+   <center>
+      <TableView fx:id="jobs" prefHeight="400.0" prefWidth="675.0" BorderPane.alignment="CENTER">
+        <columns>
+          <TableColumn prefWidth="101.0" text="Task name" />
+        </columns>
+      </TableView>
+   </center>
+</BorderPane>
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewController.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewController.java
new file mode 100644
index 0000000000000000000000000000000000000000..90cb1147eb28b679ca5014d879bd64d0ab87155a
--- /dev/null
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewController.java
@@ -0,0 +1,97 @@
+package cz.it4i.fiji.haas_spim_benchmark.ui;
+
+import java.awt.Window;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import cz.it4i.fiji.haas.ui.FXFrame;
+import cz.it4i.fiji.haas_spim_benchmark.core.BenchmarkJobManager.BenchmarkJob;
+import cz.it4i.fiji.haas_spim_benchmark.core.Constants;
+import cz.it4i.fiji.haas_spim_benchmark.core.Task;
+import cz.it4i.fiji.haas_spim_benchmark.core.TaskComputation;
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.FXML;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+
+public class SPIMPipelineProgressViewController implements FXFrame.Controller {
+
+	@FXML
+	private TableView<ObservableValue<Task>> tasks;
+
+	private BenchmarkJob job;
+	private Timer timer;
+	private ObservableTaskRegistry registry;
+
+	@Override
+	public void init(Window frame) {
+		frame.addWindowListener(new WindowAdapter() {
+			@Override
+			public void windowClosed(WindowEvent e) {
+				super.windowClosed(e);
+				dispose();
+			}
+
+		});
+		timer = new Timer();
+	}
+
+	public void setBenchmarkJob(BenchmarkJob job) {
+		this.job = job;
+		registry = new ObservableTaskRegistry(task -> tasks.getItems().remove(registry.remove(task)));
+		fillTable();
+	}
+
+	private void fillTable() {
+		List<Task> tasks = job.getTasks();
+		if (tasks == null) {
+			timer.schedule(new TimerTask() {
+				@Override
+				public void run() {
+					fillTable();
+				}
+			}, Constants.HAAS_UPDATE_TIMEOUT / 10);
+		} else {
+			List<TaskComputation> computations = tasks.stream().map(task -> task.getComputations())
+					.collect(Collectors.<List<TaskComputation>>maxBy((a, b) -> a.size() - b.size())).get();
+			int i = 0;
+			FXFrame.Controller.setCellValueFactory(this.tasks, i++, (Function<Task, String>) v -> v.getDescription());
+
+			for (TaskComputation tc : computations) {
+				this.tasks.getColumns().add(new TableColumn<>(tc.getTimepoint() + ""));
+				int index = i;
+				FXFrame.Controller.setCellValueFactory(this.tasks, i, (Function<Task, String>) v -> {
+					List<TaskComputation> items = v.getComputations();
+					if (items.size() >= index) {
+						return "";
+					} else {
+						return v.getComputations().get(index).getState().toString();
+					}
+				});
+			}
+
+			this.tasks.getItems()
+					.addAll((tasks.stream().map(task -> registry.addIfAbsent(task)).collect(Collectors.toList())));
+			timer.schedule(new TimerTask() {
+				@Override
+				public void run() {
+					updateTable();
+				}
+			}, Constants.HAAS_UPDATE_TIMEOUT, Constants.HAAS_UPDATE_TIMEOUT);
+
+		}
+	}
+
+	private void updateTable() {
+		registry.update();
+	}
+
+	private void dispose() {
+		timer.cancel();
+	}
+}
diff --git a/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewWindow.java b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..560a755c10e1e3564bafa9f70cb2f268be11d445
--- /dev/null
+++ b/haas-spim-benchmark/src/main/java/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressViewWindow.java
@@ -0,0 +1,19 @@
+package cz.it4i.fiji.haas_spim_benchmark.ui;
+
+import java.awt.Window;
+import java.io.IOException;
+
+import cz.it4i.fiji.haas.ui.FXFrame;
+import cz.it4i.fiji.haas_spim_benchmark.core.BenchmarkJobManager.BenchmarkJob;
+
+public class SPIMPipelineProgressViewWindow extends FXFrame<SPIMPipelineProgressViewController> {
+
+	private static final long serialVersionUID = 1L;
+
+	public SPIMPipelineProgressViewWindow(Window applicationFrame,BenchmarkJob job) throws IOException {
+		super(applicationFrame, "/cz/it4i/fiji/haas_spim_benchmark/ui/SPIMPipelineProgressView.fxml");
+		init(controller->controller.setBenchmarkJob(job));
+	}
+
+	
+}
diff --git a/haas-spim-benchmark/src/test/java/cz/it4i/fiji/haas/RunSPIMPipelineView.java b/haas-spim-benchmark/src/test/java/cz/it4i/fiji/haas/RunSPIMPipelineView.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b4de27fd81cb5d000089ee7ee457b189f13f59f
--- /dev/null
+++ b/haas-spim-benchmark/src/test/java/cz/it4i/fiji/haas/RunSPIMPipelineView.java
@@ -0,0 +1,14 @@
+package cz.it4i.fiji.haas;
+
+import java.io.IOException;
+
+import cz.it4i.fiji.haas_spim_benchmark.ui.SPIMPipelineProgressViewWindow;
+
+public class RunSPIMPipelineView {
+
+	public static void main(String[] args) throws IOException {
+		new SPIMPipelineProgressViewWindow(null,null).setVisible(true);
+
+	}
+
+}