diff --git a/.gitignore b/.gitignore
index 2a2b1bc2f3c3f3eec2889dc8c9fe13f4870f7542..3fde153d06b468f4673d3c1bcbfdefdeb2eef532 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 **/target/
 **/build
 **/.idea/**
-*.iml
\ No newline at end of file
+*.iml
+.idea/encodings.xml 
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index c1500a33cb5596556ab38560f449acbae8fbe473..0000000000000000000000000000000000000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="Encoding" native2AsciiForPropertiesFiles="true">
-    <file url="file://$PROJECT_DIR$" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWall" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWall/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWall/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWall/src/main/resources/config" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallComponents" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallComponents/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallComponents/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallCore" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallCore/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallCore/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginAwake/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginMedia/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginNativeAudio" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginPlayoutLog" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources" charset="UTF-8" />
-    <file url="PROJECT" charset="UTF-8" />
-  </component>
-</project>
\ No newline at end of file
diff --git a/PlayWall/pom.xml b/PlayWall/pom.xml
index e339a9b3049ae791a35ba7f8a2a48e8804854d16..1986a291c3a2e6e9e915da9222130557ca230f5b 100644
--- a/PlayWall/pom.xml
+++ b/PlayWall/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallDesktop</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWall</artifactId>
diff --git a/PlayWall/src/main/java/de/tobias/playpad/PlayPadImpl.java b/PlayWall/src/main/java/de/tobias/playpad/PlayPadImpl.java
index a8590e8ec2ffe39a877e882a444c9b82085d98f3..d393c5fad53cf6506ba688e1cd88fb2b63abfb29 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/PlayPadImpl.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/PlayPadImpl.java
@@ -4,11 +4,12 @@ import de.thecodelabs.logger.Logger;
 import de.thecodelabs.utils.application.App;
 import de.thecodelabs.utils.application.ApplicationUtils;
 import de.thecodelabs.utils.io.FileUtils;
+import de.thecodelabs.utils.io.IOUtils;
 import de.thecodelabs.utils.threading.Worker;
 import de.thecodelabs.utils.ui.NVC;
 import de.thecodelabs.utils.util.SystemUtils;
 import de.thecodelabs.versionizer.service.UpdateService;
-import de.tobias.playpad.design.ModernDesignHandler;
+import de.tobias.playpad.design.ModernDesignProvider;
 import de.tobias.playpad.initialize.*;
 import de.tobias.playpad.plugin.*;
 import de.tobias.playpad.profile.ProfileNotFoundException;
@@ -35,6 +36,8 @@ import javafx.stage.Window;
 import org.dom4j.DocumentException;
 
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
@@ -54,13 +57,15 @@ public class PlayPadImpl implements PlayPad {
 
 	private MainViewController mainViewController;
 	private Image stageIcon;
+	private byte[] stageIconData;
+
 	private Project currentProject;
 
 	private final Module module;
 
 	private UpdateService updateService;
 	protected GlobalSettings globalSettings;
-	private ModernDesignHandler modernDesign;
+	private ModernDesignProvider modernDesign;
 
 	private Session session;
 
@@ -207,6 +212,7 @@ public class PlayPadImpl implements PlayPad {
 		initializer.submit(new KeyboardDefaultMappingTask());
 
 		initializer.submit(new ServiceInitializationTask());
+		initializer.submit(new ListenerRegistrationTask());
 
 		initializer.submit(new VersionizerSetupTask());
 		initializer.submit(new ComponentLoadingTask());
@@ -234,11 +240,11 @@ public class PlayPadImpl implements PlayPad {
 		return parameters;
 	}
 
-	public ModernDesignHandler getModernDesign() {
+	public ModernDesignProvider getModernDesign() {
 		return modernDesign;
 	}
 
-	void setModernDesign(ModernDesignHandler modernDesign) {
+	void setModernDesign(ModernDesignProvider modernDesign) {
 		this.modernDesign = modernDesign;
 	}
 
@@ -281,8 +287,18 @@ public class PlayPadImpl implements PlayPad {
 		return stageIcon;
 	}
 
-	public void setIcon(Image icon) {
+	@Override
+	public byte[] getIconData() {
+		return stageIconData;
+	}
+
+	public void setIcon(Image icon, InputStream iconResource) {
 		this.stageIcon = icon;
+		try {
+			stageIconData = IOUtils.inputStreamToByteArray(iconResource);
+		} catch (IOException e) {
+			throw new UncheckedIOException(e);
+		}
 	}
 
 	public Module getModule() {
diff --git a/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java b/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java
index 79b2b7b6aae33c837fff88e2546954a85186fb52..54d75f6ffa4519698e1f250d02b654af0f49e1fe 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/PlayPadMain.java
@@ -12,7 +12,7 @@ import de.thecodelabs.utils.ui.Alerts;
 import de.thecodelabs.utils.util.OS;
 import de.thecodelabs.utils.util.OS.OSType;
 import de.thecodelabs.utils.util.SystemUtils;
-import de.tobias.playpad.design.ModernDesignHandlerImpl;
+import de.tobias.playpad.design.ModernDesignProviderImpl;
 import de.tobias.playpad.design.ModernStyleableImpl;
 import de.tobias.playpad.profile.ref.ProfileReferenceManager;
 import de.tobias.playpad.project.Project;
@@ -71,7 +71,7 @@ public class PlayPadMain extends Application {
 
 		ApplicationUtils.addAppListener(PlayPadMain::applicationWillStart);
 		App app = ApplicationUtils.registerMainApplication(PlayPadMain.class);
-		ApplicationUtils.registerUpdateSercive(new VersionUpdater());
+		ApplicationUtils.registerUpdateService(new VersionUpdater());
 
 		app.start(args);
 	}
@@ -105,10 +105,10 @@ public class PlayPadMain extends Application {
 
 		Image stageIcon = new Image(ICON_PATH);
 		Alerts.getInstance().setDefaultIcon(stageIcon);
-		impl.setIcon(stageIcon);
+		impl.setIcon(stageIcon, getClass().getClassLoader().getResourceAsStream(ICON_PATH));
 
 		PlayPadPlugin.setStyleable(new ModernStyleableImpl());
-		impl.setModernDesign(new ModernDesignHandlerImpl());
+		impl.setModernDesign(new ModernDesignProviderImpl());
 
 		PlayPadPlugin.setInstance(impl);
 	}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/RegistryCollectionImpl.java b/PlayWall/src/main/java/de/tobias/playpad/RegistryCollectionImpl.java
index d295bc188c5d73c3b030d43963c57f6d43da9343..dc7dc9e22819b80c8613fb1e636316abfb0f55c0 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/RegistryCollectionImpl.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/RegistryCollectionImpl.java
@@ -13,12 +13,12 @@ import de.tobias.playpad.view.main.MainLayoutFactory;
 
 public class RegistryCollectionImpl implements Registries {
 
-	private Registry<ActionProvider> actionRegistry;
-	private AudioRegistry audioHandlerRegistry;
-	private Registry<PadDragMode> dragModeRegistry;
-	private PadContentRegistry padContentRegistry;
-	private Registry<TriggerItemFactory> triggerItemRegistry;
-	private DefaultRegistry<MainLayoutFactory> mainLayoutRegistry;
+	private final Registry<ActionProvider> actionRegistry;
+	private final AudioRegistry audioHandlerRegistry;
+	private final Registry<PadDragMode> dragModeRegistry;
+	private final PadContentRegistry padContentRegistry;
+	private final Registry<TriggerItemFactory> triggerItemRegistry;
+	private final DefaultRegistry<MainLayoutFactory> mainLayoutRegistry;
 
 	public RegistryCollectionImpl() {
 		actionRegistry = new ComponentRegistry<>("Action");
diff --git a/PlayWall/src/main/java/de/tobias/playpad/Strings.java b/PlayWall/src/main/java/de/tobias/playpad/Strings.java
index e71f1944b156d2853018669b92e0ba6366fa9595..e7dd1a751648d3da1ab832cd2b6f0f2009a39886 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/Strings.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/Strings.java
@@ -183,6 +183,7 @@ public class Strings {
 	public static final String ACTION_PAGE_TO_STRING = "Action.Page.toString";
 	public static final String ACTION_NAVIGATE_TO_STRING = "Action.Navigate.toString";
 	public static final String ACTION_CART_NAME = "Action.Cart.Name";
+	public static final String ACTION_PLAYLIST_NEXT_NAME = "Action.PlaylistNext.Name";
 	public static final String ACTION_STOP_NAME = "Action.Stop.Name";
 	public static final String ACTION_PAGE_NAME = "Action.Page.Name";
 	public static final String ACTION_NAVIGATE_NAME = "Action.Navigate.Name";
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/PlaylistNextAction.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/PlaylistNextAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ed0a95fdd2436fcdea5ecd5c5e2df2152f6ec1c
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/PlaylistNextAction.java
@@ -0,0 +1,80 @@
+package de.tobias.playpad.action.actions;
+
+import de.thecodelabs.midi.action.Action;
+import de.thecodelabs.midi.action.ActionHandler;
+import de.thecodelabs.midi.event.KeyEvent;
+import de.thecodelabs.midi.feedback.FeedbackType;
+import de.tobias.playpad.PlayPadPlugin;
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.content.Playlistable;
+import de.tobias.playpad.project.Project;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+
+public class PlaylistNextAction extends ActionHandler {
+	public static final String TYPE = "PlaylistNextAction";
+	public static final String PAYLOAD_X = "x";
+	public static final String PAYLOAD_Y = "y";
+
+	@Override
+	public String actionType() {
+		return TYPE;
+	}
+
+	@Override
+	public FeedbackType handle(KeyEvent keyEvent, Action action) {
+		final Pad pad = getPad(action);
+		if (pad == null) {
+			return FeedbackType.NONE;
+		}
+
+		if (pad.hasVisibleContent() && pad.getContent() instanceof Playlistable) {
+			((Playlistable) pad.getContent()).next();
+		}
+		return getCurrentFeedbackType(action);
+	}
+
+	@Override
+	public FeedbackType getCurrentFeedbackType(Action action) {
+		Project project = PlayPadPlugin.getInstance().getCurrentProject();
+		IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController();
+
+		Pad pad = project.getPad(getX(action), getY(action), mainViewController.getPage());
+
+		if (pad == null || !(pad.getContent() instanceof Playlistable)) {
+			return FeedbackType.NONE;
+		}
+		return FeedbackType.DEFAULT;
+	}
+
+	public Pad getPad(Action action) {
+		Project project = PlayPadPlugin.getInstance().getCurrentProject();
+		IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController();
+
+		int x = getX(action);
+		int y = getY(action);
+		final int page = mainViewController.getPage();
+
+		return project.getPad(x, y, page);
+	}
+
+	/*
+	Property accessors
+	 */
+
+	public static int getY(Action action) {
+		return Integer.parseInt(action.getPayload(PAYLOAD_Y));
+	}
+
+	public static int getX(Action action) {
+		return Integer.parseInt(action.getPayload(PAYLOAD_X));
+	}
+
+	public static void setX(Action action, int x) {
+		action.addPayloadEntry(CartAction.PAYLOAD_X, String.valueOf(x));
+	}
+
+	public static void setY(Action action, int y) {
+		action.addPayloadEntry(CartAction.PAYLOAD_Y, String.valueOf(y));
+	}
+
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..943d6e8e9e467ffc4b5a00865e80e09b56d4502b
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.java
@@ -0,0 +1,10 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.thecodelabs.midi.event.KeyEventType;
+import de.tobias.playpad.action.actions.CartAction;
+import de.tobias.playpad.pad.Pad;
+
+public interface CartActionHandler
+{
+	void performAction(KeyEventType keyEventType, CartAction cartAction, Pad pad);
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..39e45b2d81e9373683f976647730f58d964bf744
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.java
@@ -0,0 +1,27 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.tobias.playpad.action.actions.CartAction;
+
+public class CartActionHandlerFactory
+{
+	private CartActionHandlerFactory()
+	{
+	}
+
+	public static CartActionHandler getInstance(CartAction.CartActionMode cartActionMode)
+	{
+		switch(cartActionMode)
+		{
+			case PLAY_PAUSE:
+				return new PlayPauseHandler();
+			case PLAY_STOP:
+				return new PlayStopHandler();
+			case PLAY_HOLD:
+				return new PlayHoldHandler();
+			case PLAY_PLAY:
+				return new PlayPlayHandler();
+			default:
+				return null;
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..164e7977f7a22f9ee44143d4d105762444f9febd
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.java
@@ -0,0 +1,27 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.thecodelabs.midi.event.KeyEventType;
+import de.tobias.playpad.action.actions.CartAction;
+import de.tobias.playpad.pad.Pad;
+
+public class PlayHoldHandler implements CartActionHandler
+{
+	@Override
+	public void performAction(KeyEventType keyEventType, CartAction cartAction, Pad pad)
+	{
+		if(keyEventType.equals(KeyEventType.DOWN))
+		{
+			if(pad.isReady())
+			{
+				pad.play();
+			}
+		}
+		else if(keyEventType.equals(KeyEventType.UP))
+		{
+			if(pad.isPlay())
+			{
+				pad.stop();
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..859ececd5d4e9d1cadefb2d89812ff97973f789d
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.java
@@ -0,0 +1,25 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.thecodelabs.midi.event.KeyEventType;
+import de.tobias.playpad.action.actions.CartAction;
+import de.tobias.playpad.pad.Pad;
+
+public class PlayPauseHandler implements CartActionHandler
+{
+	@Override
+	public void performAction(KeyEventType keyEventType, CartAction cartAction, Pad pad)
+	{
+		if(keyEventType.equals(KeyEventType.DOWN))
+		{
+			if(pad.isPlay())
+			{
+				pad.pause();
+			}
+			else
+			{ // Allow the listener to send the feedback
+				// cartAction.getPadPositionListener.setSend(false) TODO Fix
+				pad.play();
+			}
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..87b716988a04dec294b71d43afe0e1b1740ab6ba
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.java
@@ -0,0 +1,19 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.thecodelabs.midi.event.KeyEventType;
+import de.tobias.playpad.action.actions.CartAction;
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.PadStatus;
+
+public class PlayPlayHandler implements CartActionHandler
+{
+
+	@Override
+	public void performAction(KeyEventType keyEventType, CartAction cartAction, Pad pad)
+	{
+		if(keyEventType.equals(KeyEventType.DOWN))
+		{
+			pad.setStatus(PadStatus.RESTART);
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.java b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..091f28a5cc9e9f43d577aa42c1b6c3f92a136f31
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.java
@@ -0,0 +1,25 @@
+package de.tobias.playpad.action.actions.cart.handler;
+
+import de.thecodelabs.midi.event.KeyEventType;
+import de.tobias.playpad.action.actions.CartAction;
+import de.tobias.playpad.pad.Pad;
+
+public class PlayStopHandler implements CartActionHandler
+{
+	@Override
+	public void performAction(KeyEventType keyEventType, CartAction cartAction, Pad pad)
+	{
+		if(keyEventType.equals(KeyEventType.DOWN))
+		{
+			if(pad.isPlay())
+			{
+				pad.stop();
+			}
+			else
+			{ // Allow the listener to send the feedback
+				// cartAction.getPadPositionListener.setSend(false) TODO Fix
+				pad.play();
+			}
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/factory/CartActionProvider.java b/PlayWall/src/main/java/de/tobias/playpad/action/factory/CartActionProvider.java
index 7b078335255b168e0d61f42051ea0ab5cea69afd..43e38b7c61c6d0badc69bb0fcd92c15bfdc6deff 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/action/factory/CartActionProvider.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/factory/CartActionProvider.java
@@ -8,7 +8,6 @@ import de.thecodelabs.midi.mapping.KeyType;
 import de.tobias.playpad.action.ActionProvider;
 import de.tobias.playpad.action.ActionType;
 import de.tobias.playpad.action.actions.CartAction;
-import de.tobias.playpad.action.actions.CartAction.*;
 import de.tobias.playpad.action.settings.ActionSettingsEntry;
 import de.tobias.playpad.action.settings.CartActionSettingsEntry;
 import de.tobias.playpad.project.ProjectSettings;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/factory/PlaylistNextActionProvider.java b/PlayWall/src/main/java/de/tobias/playpad/action/factory/PlaylistNextActionProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f317e7e0b7ab25ca1cb21348373e457f03bb4dd9
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/factory/PlaylistNextActionProvider.java
@@ -0,0 +1,73 @@
+package de.tobias.playpad.action.factory;
+
+import de.thecodelabs.midi.Mapping;
+import de.thecodelabs.midi.action.Action;
+import de.thecodelabs.midi.action.ActionHandler;
+import de.thecodelabs.midi.feedback.FeedbackType;
+import de.thecodelabs.midi.mapping.KeyType;
+import de.tobias.playpad.action.ActionProvider;
+import de.tobias.playpad.action.ActionType;
+import de.tobias.playpad.action.actions.PlaylistNextAction;
+import de.tobias.playpad.action.settings.ActionSettingsEntry;
+import de.tobias.playpad.action.settings.PlaylistNextActionSettingsEntry;
+import de.tobias.playpad.project.ProjectSettings;
+import javafx.scene.control.TreeItem;
+
+import java.util.List;
+
+import static de.tobias.playpad.action.actions.PlaylistNextAction.*;
+
+public class PlaylistNextActionProvider extends ActionProvider {
+
+	public PlaylistNextActionProvider(String type) {
+		super(TYPE);
+	}
+
+	@Override
+	public String getType() {
+		return TYPE;
+	}
+
+	@Override
+	public ActionHandler getActionHandler() {
+		return new PlaylistNextAction();
+	}
+
+	@Override
+	public void createDefaultActions(Mapping mapping) {
+		for (int x = 0; x < ProjectSettings.MAX_COLUMNS; x++) {
+			for (int y = 0; y < ProjectSettings.MAX_ROWS; y++) {
+				Action action = newInstance(x, y);
+				mapping.addUniqueAction(action);
+			}
+		}
+	}
+
+	private Action newInstance(int x, int y) {
+		Action action = new Action(getType());
+		action.addPayloadEntry(PAYLOAD_X, String.valueOf(x));
+		action.addPayloadEntry(PAYLOAD_Y, String.valueOf(y));
+		return action;
+	}
+
+	@Override
+	public FeedbackType[] supportedFeedbackOptions(Action action, KeyType keyType) {
+		switch (keyType) {
+			case KEYBOARD:
+				return new FeedbackType[0];
+			case MIDI:
+				return new FeedbackType[]{FeedbackType.DEFAULT};
+		}
+		return new FeedbackType[0];
+	}
+
+	@Override
+	public ActionType getActionType() {
+		return ActionType.CONTROL;
+	}
+
+	@Override
+	public TreeItem<ActionSettingsEntry> getTreeItemForActions(List<Action> actions, Mapping mapping) {
+		return new TreeItem<>(new PlaylistNextActionSettingsEntry());
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/settings/CartActionSettingsEntry.java b/PlayWall/src/main/java/de/tobias/playpad/action/settings/CartActionSettingsEntry.java
index ea3451324c18430407d74b4a7ab10b698748057a..725a321b99527e0f06e4ce99adb1739040a332b7 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/action/settings/CartActionSettingsEntry.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/settings/CartActionSettingsEntry.java
@@ -5,8 +5,10 @@ import de.thecodelabs.utils.ui.NVC;
 import de.thecodelabs.utils.ui.icon.FontIcon;
 import de.thecodelabs.utils.util.Localization;
 import de.tobias.playpad.Strings;
+import de.tobias.playpad.action.actions.CartAction;
 import de.tobias.playpad.viewcontroller.IMappingTabViewController;
 import de.tobias.playpad.viewcontroller.actions.CartActionTypeViewController;
+import de.tobias.playpad.viewcontroller.actions.CartActionViewController;
 
 public class CartActionSettingsEntry implements ActionSettingsEntry {
 
@@ -22,6 +24,6 @@ public class CartActionSettingsEntry implements ActionSettingsEntry {
 
 	@Override
 	public NVC getDetailSettingsController(Mapping mapping, IMappingTabViewController controller) {
-		return new CartActionTypeViewController(mapping, controller);
+		return new CartActionTypeViewController(mapping, controller, CartAction.TYPE, new CartActionViewController());
 	}
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/action/settings/PlaylistNextActionSettingsEntry.java b/PlayWall/src/main/java/de/tobias/playpad/action/settings/PlaylistNextActionSettingsEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..8548c9d59ec156307007d3d212e249123ab46866
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/action/settings/PlaylistNextActionSettingsEntry.java
@@ -0,0 +1,28 @@
+package de.tobias.playpad.action.settings;
+
+import de.thecodelabs.midi.Mapping;
+import de.thecodelabs.utils.ui.NVC;
+import de.thecodelabs.utils.ui.icon.FontIcon;
+import de.thecodelabs.utils.util.Localization;
+import de.tobias.playpad.Strings;
+import de.tobias.playpad.action.actions.PlaylistNextAction;
+import de.tobias.playpad.viewcontroller.IMappingTabViewController;
+import de.tobias.playpad.viewcontroller.actions.CartActionTypeViewController;
+
+public class PlaylistNextActionSettingsEntry implements ActionSettingsEntry {
+
+	@Override
+	public String getName() {
+		return Localization.getString(Strings.ACTION_PLAYLIST_NEXT_NAME);
+	}
+
+	@Override
+	public FontIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public NVC getDetailSettingsController(Mapping mapping, IMappingTabViewController controller) {
+		return new CartActionTypeViewController(mapping, controller, PlaylistNextAction.TYPE);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernCartDesignHandlerImpl.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernCartDesignHandlerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..3345771403815a048d53a6f4935ecc11f442e4e5
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernCartDesignHandlerImpl.java
@@ -0,0 +1,106 @@
+package de.tobias.playpad.design;
+
+import de.thecodelabs.utils.application.ApplicationUtils;
+import de.thecodelabs.utils.application.resources.classpath.ClasspathResource;
+import de.tobias.playpad.design.modern.ModernCartDesignHandler;
+import de.tobias.playpad.design.modern.ModernColor;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
+import de.tobias.playpad.util.Minifier;
+import de.tobias.playpad.view.PseudoClasses;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ModernCartDesignHandlerImpl implements ModernCartDesignHandler
+{
+	@Override
+	public String generateCss(ModernCartDesign design, String classSuffix, boolean flat)
+	{
+		String result = "";
+
+		if(design.isEnableCustomBackgroundColor())
+		{
+			result += generatePadCss(design, flat, classSuffix, design.getBackgroundColor(), "");
+			result += generatePadCss(design, flat, classSuffix, design.getBackgroundColor(), MessageFormat.format(":{0}", PseudoClasses.WARN_CLASS.getPseudoClassName()));
+		}
+
+		if(design.isEnableCustomPlayColor())
+		{
+			result += generatePadCss(design, flat, classSuffix, design.getPlayColor(), MessageFormat.format(":{0}", PseudoClasses.PLAY_CLASS.getPseudoClassName()));
+			;
+		}
+
+		if(design.isEnableCustomCueInColor())
+		{
+			result += generateCueInCss(design, flat, classSuffix);
+		}
+
+		return result;
+	}
+
+	private String generatePadCss(ModernCartDesign design, Boolean flat, String padIdentifier, ModernColor color, String styleState)
+	{
+		final Map<String, Object> values = new HashMap<>();
+		values.put("prefix", padIdentifier);
+		values.put("class", styleState);
+		values.put("buttonColor", color.getButtonColor());
+		values.put("playbarTrackColor", color.getPlaybarColor());
+		values.put("playbarBarColor", color.getPlaybarTrackColor());
+
+		if(flat)
+		{
+			values.put("padColor", color.paint());
+		}
+		else
+		{
+			values.put("padColor", color.linearGradient());
+		}
+
+		if(flat)
+		{
+			values.put("padCueInColor", design.getCueInColor().paint());
+		}
+		else
+		{
+			values.put("padCueInColor", design.getCueInColor().linearGradient());
+		}
+
+		values.put("fontColor", color.getFontColor());
+
+		return generateCss("style/modern-pad.css", values);
+	}
+
+	private String generateCueInCss(ModernCartDesign design, Boolean flat, String padIdentifier)
+	{
+		final Map<String, Object> values = new HashMap<>();
+		values.put("prefix", padIdentifier);
+		if(flat)
+		{
+			values.put("padCueInColor", design.getCueInColor().paint());
+		}
+		else
+		{
+			values.put("padCueInColor", design.getCueInColor().linearGradient());
+		}
+
+		return generateCss("style/modern-pad-cue-in.css", values);
+	}
+
+	private String generateCss(String templatePath, Map<String, Object> values)
+	{
+		final ExpressionParser expressionParser = new SpelExpressionParser();
+
+		final StandardEvaluationContext context = new StandardEvaluationContext();
+
+		final ClasspathResource resource = ApplicationUtils.getApplication().getClasspathResource(templatePath);
+		final String content = Minifier.minify(resource.getAsString());
+
+		context.setVariables(values);
+		return expressionParser.parseExpression(content, new TemplateParserContext("${", "}")).getValue(context, String.class);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java
index ffd70e7b8b949a039aeb21718fdd335de4193f16..08b99ef1e97ccbc31b46fc0622c5a00c119d4f5a 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignAnimator.java
@@ -1,7 +1,7 @@
 package de.tobias.playpad.design;
 
 import de.tobias.playpad.pad.view.IPadView;
-import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
+import de.tobias.playpad.project.api.IPad;
 import de.tobias.playpad.util.FadeableColor;
 import de.tobias.playpad.view.PseudoClasses;
 import javafx.animation.KeyFrame;
@@ -17,22 +17,21 @@ import java.util.HashMap;
 
 public class ModernDesignAnimator {
 
-	// alles nur static, neine objecte von der Klasse
 	private ModernDesignAnimator() {
 	}
 
-	private static HashMap<Integer, Timeline> timelines = new HashMap<>();
+	private static final HashMap<Integer, Timeline> timelines = new HashMap<>();
 
-	public static void animateFade(AbstractPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) {
+	public static void animateFade(IPad pad, IPadView padView, FadeableColor startColor, FadeableColor endColor, Duration duration) {
 		ObjectProperty<FadeableColor> backgroundColor = new SimpleObjectProperty<>();
 
 		Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0), new KeyValue(backgroundColor, startColor)),
 				new KeyFrame(duration, new KeyValue(backgroundColor, endColor)));
 
-		animate(padViewController, timeline, backgroundColor);
+		animate(pad, padView, timeline, backgroundColor);
 	}
 
-	public static void animateWarn(AbstractPadViewController padViewController, FadeableColor startColor, FadeableColor endColor, Duration duration) {
+	public static void animateWarn(IPad pad, IPadView padView, FadeableColor startColor, FadeableColor endColor, Duration duration) {
 		ObjectProperty<FadeableColor> backgroundColor = new SimpleObjectProperty<>();
 
 		Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0), new KeyValue(backgroundColor, startColor)),
@@ -42,17 +41,17 @@ public class ModernDesignAnimator {
 
 		timeline.setAutoReverse(true);
 		timeline.setCycleCount((int) (duration.toSeconds() / 0.625));
-		animate(padViewController, timeline, backgroundColor);
+		animate(pad, padView, timeline, backgroundColor);
 	}
 
-	private static void animate(AbstractPadViewController padViewController, Timeline timeline, ObjectProperty<FadeableColor> objectProperty) {
-		int index = padViewController.getPad().getPosition();
+	private static void animate(IPad pad, IPadView padView, Timeline timeline, ObjectProperty<FadeableColor> objectProperty) {
+		int index = pad.getPosition();
 
 		if (timelines.containsKey(index)) {
 			timelines.get(index).stop();
 		}
 
-		ChangeListener<FadeableColor> fadeListener = (observable, oldValue, newValue) -> padViewController.getView().setStyle("-fx-background-color: " + newValue.toString() + ";");
+		ChangeListener<FadeableColor> fadeListener = (observable, oldValue, newValue) -> padView.setStyle("-fx-background-color: " + newValue.toString() + ";");
 		objectProperty.addListener(fadeListener);
 
 		timeline.playFromStart();
@@ -60,7 +59,7 @@ public class ModernDesignAnimator {
 		timeline.setOnFinished(event ->
 		{
 			objectProperty.removeListener(fadeListener);
-			padViewController.getView().setStyle("");
+			padView.setStyle("");
 			timelines.remove(index);
 		});
 
@@ -68,16 +67,15 @@ public class ModernDesignAnimator {
 		timelines.put(index, timeline);
 	}
 
-	public static void stopAnimation(AbstractPadViewController controller) {
-		int index = controller.getPad().getPosition();
+	public static void stopAnimation(IPad pad) {
+		int index = pad.getPosition();
 
 		if (timelines.containsKey(index)) {
 			timelines.get(index).stop();
 		}
 	}
 
-	public static void warnFlash(AbstractPadViewController controller) {
-		final IPadView view = controller.getView();
+	public static void warnFlash(IPadView view) {
 		try {
 			while (!Thread.interrupted()) {
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignHandlerImpl.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignProviderImpl.java
similarity index 63%
rename from PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignHandlerImpl.java
rename to PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignProviderImpl.java
index 4d14df99a3f684c79a02dbfa75b37b259b20a9ff..c1d2a5521b5de332a8efdfc3f2eaac933dcc6117 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignHandlerImpl.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernDesignProviderImpl.java
@@ -2,12 +2,22 @@ package de.tobias.playpad.design;
 
 import de.tobias.playpad.design.modern.ModernCartDesignHandler;
 import de.tobias.playpad.design.modern.ModernGlobalDesignHandler;
+import de.tobias.playpad.design.modern.ModernWarningDesignHandler;
 
-public class ModernDesignHandlerImpl implements ModernDesignHandler {
+public class ModernDesignProviderImpl implements ModernDesignProvider {
 
+	private ModernWarningDesignHandler warningHandler;
 	private ModernCartDesignHandler cartDesignHandler;
 	private ModernGlobalDesignHandler globalDesignHandler;
 
+	@Override
+	public ModernWarningDesignHandler warning() {
+		if (warningHandler == null) {
+			warningHandler = new ModernWarningDesignHandlerImpl();
+		}
+		return warningHandler;
+	}
+
 	@Override
 	public ModernGlobalDesignHandler global() {
 		if (globalDesignHandler == null) {
diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cde8dfaa882fb758ef6773bc3c69093314f906c
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.java
@@ -0,0 +1,148 @@
+package de.tobias.playpad.design;
+
+import de.thecodelabs.logger.Logger;
+import de.thecodelabs.utils.application.ApplicationUtils;
+import de.thecodelabs.utils.application.container.PathType;
+import de.thecodelabs.utils.application.resources.classpath.ClasspathResource;
+import de.tobias.playpad.DisplayableColor;
+import de.tobias.playpad.PlayPadMain;
+import de.tobias.playpad.design.modern.ModernCartDesignHandler;
+import de.tobias.playpad.design.modern.ModernColor;
+import de.tobias.playpad.design.modern.ModernGlobalDesignHandler;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
+import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
+import de.tobias.playpad.pad.PadSettings;
+import de.tobias.playpad.project.Project;
+import de.tobias.playpad.util.Minifier;
+import de.tobias.playpad.view.ColorPickerView;
+import de.tobias.playpad.view.PseudoClasses;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+import javafx.scene.Node;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ModernGlobalDesignHandlerImpl implements ModernGlobalDesignHandler, ColorModeHandler
+{
+	@Override
+	public void applyStyleSheet(Stage stage)
+	{
+		stage.getScene().getStylesheets().add("style/style.css");
+		stage.getScene().getStylesheets().add("style/modern.css");
+
+		// Custom style for playwall available
+		final Path customCss = ApplicationUtils.getApplication().getPath(PathType.CONFIGURATION, "style.css");
+		if(Files.exists(customCss))
+		{
+			stage.getScene().getStylesheets().add(customCss.toUri().toString());
+		}
+	}
+
+	@Override
+	public void applyStyleSheetToMainViewController(ModernGlobalDesign design, IMainViewController controller, Stage stage, Project project)
+	{
+		applyStyleSheet(stage);
+
+		controller.setGridColor(Color.TRANSPARENT);
+
+		// generate dynamic style sheet
+		final Path customCss = ApplicationUtils.getApplication().getPath(PathType.CONFIGURATION, "custom_style.css");
+		final StringBuilder stringBuilder = new StringBuilder();
+		stringBuilder.append(generateCss(design));
+
+		final ModernCartDesignHandler cartDesignHandler = PlayPadMain.getProgramInstance().getModernDesign().cart();
+		project.getPage(controller.getPage()).getPads().forEach(pad -> {
+			final PadSettings padSettings = pad.getPadSettings();
+
+			final ModernCartDesign cartDesign = padSettings.getDesign();
+			stringBuilder.append(cartDesignHandler.generateCss(cartDesign, String.valueOf(pad.getPadIndex()), design.isFlatDesign()));
+		});
+
+		try
+		{
+			Files.write(customCss, stringBuilder.toString().getBytes());
+		}
+		catch(IOException e)
+		{
+			Logger.error(e);
+		}
+
+		stage.getScene().getStylesheets().remove(customCss.toUri().toString());
+		stage.getScene().getStylesheets().add(customCss.toUri().toString());
+	}
+
+	private String generateCss(ModernGlobalDesign design)
+	{
+		return String.join(
+				generateCss(design, design.getBackgroundColor(), ""),
+				generateCss(design, design.getPlayColor(), MessageFormat.format(":{0}", PseudoClasses.PLAY_CLASS.getPseudoClassName())),
+				generateCss(design, design.getBackgroundColor(), MessageFormat.format(":{0}", PseudoClasses.WARN_CLASS.getPseudoClassName()))
+		);
+	}
+
+	private String generateCss(ModernGlobalDesign design, ModernColor color, String styleState)
+	{
+		final ExpressionParser expressionParser = new SpelExpressionParser();
+		final StandardEvaluationContext context = new StandardEvaluationContext();
+
+		final ClasspathResource resource = ApplicationUtils.getApplication().getClasspathResource("style/modern-global.css");
+		final String content = Minifier.minify(resource.getAsString());
+
+		final Map<String, Object> values = new HashMap<>();
+		values.put("class", styleState);
+		values.put("buttonColor", color.getButtonColor());
+		values.put("playbarTrackColor", color.getPlaybarColor());
+		values.put("playbarBarColor", color.getPlaybarTrackColor());
+
+		if(design.isFlatDesign())
+		{
+			values.put("padColor", color.paint());
+		}
+		else
+		{
+			values.put("padColor", color.linearGradient());
+		}
+
+		if(design.isFlatDesign())
+		{
+			values.put("padCueInColor", design.getCueInColor().paint());
+		}
+		else
+		{
+			values.put("padCueInColor", design.getCueInColor().linearGradient());
+		}
+
+		values.put("fontColor", color.getFontColor());
+		values.put("infoFontSize", String.valueOf(design.getInfoFontSize()));
+		values.put("titleFontSize", String.valueOf(design.getTitleFontSize()));
+
+		context.setVariables(values);
+		return expressionParser.parseExpression(content, new TemplateParserContext("${", "}")).getValue(context, String.class);
+	}
+
+	@Override
+	public Node getColorInterface(Consumer<DisplayableColor> onSelection)
+	{
+		return new ColorPickerView(null, ModernColor.values(), onSelection);
+	}
+
+	@Override
+	public void setColor(ModernCartDesign design, DisplayableColor color)
+	{
+		if(color instanceof ModernColor)
+		{
+			design.setBackgroundColor((ModernColor) color);
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/design/ModernWarningDesignHandlerImpl.java b/PlayWall/src/main/java/de/tobias/playpad/design/ModernWarningDesignHandlerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..609f6581faa0dacec65ca1028a3b0385a7ed1d75
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/design/ModernWarningDesignHandlerImpl.java
@@ -0,0 +1,30 @@
+package de.tobias.playpad.design;
+
+import de.tobias.playpad.design.modern.ModernWarningDesignHandler;
+import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
+import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
+import de.tobias.playpad.project.api.IPad;
+import de.tobias.playpad.util.FadeableColor;
+import javafx.util.Duration;
+
+public class ModernWarningDesignHandlerImpl implements ModernWarningDesignHandler
+{
+	@Override
+	public void performWarning(ModernGlobalDesign design, FadeableColor fadeStartColor, FadeableColor fadeStopColor, AbstractPadViewController controller, Duration warningDuration)
+	{
+		if(design.isWarnAnimation())
+		{
+			ModernDesignAnimator.animateWarn(controller.getPad(), controller.getView(), fadeStartColor, fadeStopColor, warningDuration);
+		}
+		else
+		{
+			ModernDesignAnimator.warnFlash(controller.getView());
+		}
+	}
+
+	@Override
+	public void stopWarning(IPad pad)
+	{
+		ModernDesignAnimator.stopAnimation(pad);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializeTask.java b/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializeTask.java
index c979364d11375530570ee5ae60705a9c9e89e194..012c8173a7d06cf73908f455c0a32073f8f8c180 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializeTask.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializeTask.java
@@ -5,5 +5,6 @@ import de.tobias.playpad.PlayPadImpl;
 
 public interface PlayPadInitializeTask {
 	String name();
+
 	void run(App app, PlayPadImpl instance);
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializer.java b/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializer.java
index dc4c726337a65c963cd0760b377d329191e662ec..133ef5b9110634ecd6a793e8fa618858181a3a80 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializer.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/initialize/PlayPadInitializer.java
@@ -24,10 +24,10 @@ public class PlayPadInitializer implements Runnable {
 		void errorLoading(PlayPadInitializeTask task, Exception e);
 	}
 
-	private List<PlayPadInitializeTask> tasks;
+	private final List<PlayPadInitializeTask> tasks;
 
-	private PlayPadImpl instance;
-	private Listener listener;
+	private final PlayPadImpl instance;
+	private final Listener listener;
 
 	public PlayPadInitializer(PlayPadImpl instance, Listener listener) {
 		tasks = new ArrayList<>();
@@ -42,6 +42,9 @@ public class PlayPadInitializer implements Runnable {
 	public void start() {
 		Thread thread = new Thread(this);
 		thread.setName("PlayPad Initializer Thread");
+		thread.setUncaughtExceptionHandler((t, e) -> {
+			Logger.error(e);
+		});
 		thread.start();
 	}
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopColorPickerView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopColorPickerView.java
index b71c1305615c898f1db7f4d3bf7d5f443116c5a9..0e3de9499da41130e72e786773960f9a09cccd8e 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopColorPickerView.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopColorPickerView.java
@@ -66,11 +66,11 @@ public class DesktopColorPickerView implements Consumer<DisplayableColor>, Event
 				PadSettings padSettings = pad.getPadSettings();
 
 				if (event.getButton() == MouseButton.PRIMARY) {
-					padSettings.setCustomDesign(true);
 					ModernCartDesign design = padSettings.getDesign();
+					design.setEnableCustomBackgroundColor(true);
 					colorModeHandler.setColor(design, selectedColor);
 				} else if (event.getButton() == MouseButton.SECONDARY) {
-					padSettings.setCustomDesign(false);
+					padSettings.getDesign().setEnableCustomBackgroundColor(false);
 				}
 				PlayPadMain.getProgramInstance().getMainViewController().loadUserCss();
 			}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
index aacddf3f434ca9ed7d05381521cfaeb3d870b95b..2f3410e6c6596dfcd5d9e0166054c124c8a2f14a 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/DesktopMenuToolbarViewController.java
@@ -21,7 +21,6 @@ import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.profile.ProfileNotFoundException;
 import de.tobias.playpad.profile.ProfileSettings;
 import de.tobias.playpad.profile.ref.ProfileReference;
-import de.tobias.playpad.profile.ref.ProfileReferenceManager;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.project.ProjectNotFoundException;
 import de.tobias.playpad.project.ProjectReader.ProjectReaderDelegate.ProfileAbortException;
@@ -148,7 +147,7 @@ public class DesktopMenuToolbarViewController extends BasicMenuToolbarViewContro
 	private ToggleButton colorButton;
 	private Button addPageButton;
 
-	private IMainViewController mainViewController;
+	private final IMainViewController mainViewController;
 
 	private transient ProjectSettingsViewController projectSettingsViewController;
 	private transient ProfileSettingsViewController profileSettingsViewController;
@@ -553,18 +552,7 @@ public class DesktopMenuToolbarViewController extends BasicMenuToolbarViewContro
 
 	@FXML
 	void saveMenuHandler(ActionEvent event) {
-		try {
-			ProjectReferenceManager.saveProjects();
-			ProjectReferenceManager.saveSingleProject(openProject);
-			ProfileReferenceManager.saveProfiles();
-			Profile.currentProfile().save();
-			PlayPadPlugin.getInstance().getGlobalSettings().save();
-
-			mainViewController.notify(Localization.getString(Strings.STANDARD_FILE_SAVE), PlayPadMain.NOTIFICATION_DISPLAY_TIME);
-		} catch (IOException e) {
-			mainViewController.showError(Localization.getString(Strings.ERROR_PROJECT_SAVE));
-			Logger.error(e);
-		}
+		mainViewController.save();
 	}
 
 	@FXML
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
index e1489f3445343db9ef7b1b3a46be0e6030a8f690..89968e77a9c79ae23d8d5f6f54728f2f2d791042 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
@@ -8,6 +8,7 @@ import de.thecodelabs.utils.ui.scene.BusyView;
 import de.thecodelabs.utils.util.ColorUtils;
 import de.tobias.playpad.PlayPadPlugin;
 import de.tobias.playpad.design.FeedbackDesignColorSuggester;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
 import de.tobias.playpad.layout.desktop.DesktopMainLayoutFactory;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.PadStatus;
@@ -354,19 +355,18 @@ public class DesktopPadView implements IPadView {
 	@Override
 	public void showNotFoundIcon(Pad pad, boolean show) {
 		if (show) {
-			FeedbackDesignColorSuggester associator;
-			if (pad.getPadSettings().isCustomDesign()) {
-				associator = pad.getPadSettings().getDesign();
-			} else {
-				associator = Profile.currentProfile().getProfileSettings().getDesign();
-			}
+			FeedbackDesignColorSuggester globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
+			Color layoutStdColor = globalDesign.getDesignDefaultColor();
 
-			if (associator != null) {
-				Color color = associator.getDesignDefaultColor();
-				notFoundLabel.setColor(ColorUtils.getAppropriateTextColor(color));
-			} else {
-				notFoundLabel.setColor(Color.RED);
+			if (pad != null) {
+				final ModernCartDesign padDesign = pad.getPadSettings().getDesign();
+
+				if (padDesign.isEnableCustomBackgroundColor()) {
+					layoutStdColor = padDesign.getDesignDefaultColor();
+				}
 			}
+
+			notFoundLabel.setColor(ColorUtils.getAppropriateTextColor(layoutStdColor));
 		}
 		notFoundLabel.setVisible(show);
 		root.setOpacity(show ? 0.5 : 1.0);
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java
index fd9acc77f4555699721cdea533a759b8a673a3fd..4791b906a5d4591b612e9729a1d27558fb13106c 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadViewController.java
@@ -34,6 +34,8 @@ public class DesktopPadViewController extends AbstractPadViewController implemen
 	private final DesktopPadView padView;
 	private Pad pad;
 
+	private PadSettingsViewController padSettingsViewController;
+
 	private final PadLockedListener padLockedListener;
 	private final PadStatusListener padStatusListener;
 	private final PadContentListener padContentListener;
@@ -204,21 +206,26 @@ public class DesktopPadViewController extends AbstractPadViewController implemen
 	}
 
 	private void onSettings() {
-		GlobalSettings settings = PlayPadPlugin.getInstance().getGlobalSettings();
-		IMainViewController mvc = PlayPadPlugin.getInstance().getMainViewController();
+		final GlobalSettings globalSettings = PlayPadPlugin.getInstance().getGlobalSettings();
+		final IMainViewController mvc = PlayPadPlugin.getInstance().getMainViewController();
 
 		if (mvc != null) {
-			if (pad.getProject() != null && settings.isLiveMode() && settings.isLiveModeSettings() && pad.getProject().getActivePlayers() > 0) {
+			if (pad.getProject() != null && globalSettings.isLiveMode() && globalSettings.isLiveModeSettings() && pad.getProject().getActivePlayers() > 0) {
 				return;
 			}
 
-			PadSettingsViewController padSettingsViewController = new PadSettingsViewController(pad, mvc);
-			padSettingsViewController.getStageContainer().ifPresent(nvcStage -> nvcStage.addCloseHook(() -> {
-				if (padView != null && pad != null)
-					padView.setTriggerLabelActive(pad.getPadSettings().hasTriggerItems());
-				return true;
-			}));
-			padSettingsViewController.getStageContainer().ifPresent(NVCStage::show);
+			if (padSettingsViewController == null) {
+				padSettingsViewController = new PadSettingsViewController(pad, mvc);
+				padSettingsViewController.getStageContainer().ifPresent(nvcStage -> nvcStage.addCloseHook(() -> {
+					if (padView != null && pad != null)
+						padView.setTriggerLabelActive(pad.getPadSettings().hasTriggerItems());
+					padSettingsViewController = null;
+					return true;
+				}));
+				padSettingsViewController.getStageContainer().ifPresent(NVCStage::show);
+			} else {
+				padSettingsViewController.getStageContainer().ifPresent(stage -> stage.getStage().toFront());
+			}
 		}
 	}
 
@@ -261,7 +268,6 @@ public class DesktopPadViewController extends AbstractPadViewController implemen
 			}
 		}
 		padView.setPlayBarProgress(0);
-		padView.setCueInProgress(0);
 		padView.setTime(null);
 	}
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/TouchMenuToolbarViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/TouchMenuToolbarViewController.java
index 51f0e31995aa4d5ab5744a4fde399b749e1a73c0..3fb75001df01637862485a3527bba1ff909218f0 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/TouchMenuToolbarViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/TouchMenuToolbarViewController.java
@@ -157,7 +157,6 @@ public class TouchMenuToolbarViewController extends BasicMenuToolbarViewControll
 	}
 
 	@Override
-	public void initLayoutMenu()
-	{
+	public void initLayoutMenu() {
 	}
 }
\ No newline at end of file
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java
index 3c220b8ba255b641ea8355d2cc818418e90e1a5f..4ba78420683b47d6ee938cf836364b085f80261d 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadView.java
@@ -1,6 +1,7 @@
 package de.tobias.playpad.layout.touch.pad;
 
 import de.thecodelabs.logger.Logger;
+import de.thecodelabs.utils.ui.animation.PulseTranslation;
 import de.thecodelabs.utils.ui.icon.FontAwesomeType;
 import de.thecodelabs.utils.ui.icon.FontIcon;
 import de.thecodelabs.utils.ui.scene.BusyView;
@@ -9,6 +10,7 @@ import de.thecodelabs.utils.util.OS;
 import de.thecodelabs.utils.util.win.User32X;
 import de.tobias.playpad.PlayPadPlugin;
 import de.tobias.playpad.design.FeedbackDesignColorSuggester;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.content.PadContent;
 import de.tobias.playpad.pad.content.PadContentFactory;
@@ -242,7 +244,8 @@ public class TouchPadView implements IPadView {
 
 	@Override
 	public void highlightView(int milliSeconds) {
-		cueInLayer.setPrefWidth(root.getWidth() * milliSeconds);
+		PulseTranslation pulseTranslation = new PulseTranslation(superRoot, null, 0.1);
+		pulseTranslation.play();
 	}
 
 	void clearIndex() {
@@ -282,19 +285,18 @@ public class TouchPadView implements IPadView {
 	@Override
 	public void showNotFoundIcon(Pad pad, boolean show) {
 		if (show) {
-			FeedbackDesignColorSuggester associator = null;
-			if (pad.getPadSettings().isCustomDesign()) {
-				associator = pad.getPadSettings().getDesign();
-			} else {
-				associator = Profile.currentProfile().getProfileSettings().getDesign();
-			}
+			FeedbackDesignColorSuggester globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
+			Color layoutStdColor = globalDesign.getDesignDefaultColor();
+
+			if (pad != null) {
+				final ModernCartDesign padDesign = pad.getPadSettings().getDesign();
 
-			if (associator != null) {
-				Color color = associator.getDesignDefaultColor();
-				notFoundLabel.setColor(ColorUtils.getAppropriateTextColor(color));
-			} else {
-				notFoundLabel.setColor(Color.RED);
+				if (padDesign.isEnableCustomBackgroundColor()) {
+					layoutStdColor = padDesign.getDesignDefaultColor();
+				}
 			}
+
+			notFoundLabel.setColor(ColorUtils.getAppropriateTextColor(layoutStdColor));
 		}
 		notFoundLabel.setVisible(show);
 		root.setOpacity(show ? 0.5 : 1.0);
diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java
index c43b9679075c400a0e949b80e2b9ab64e28d4f50..11d6808442e2ac1bbd0861565a4ce1f78687ff2c 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/touch/pad/TouchPadViewController.java
@@ -23,14 +23,14 @@ public class TouchPadViewController extends AbstractPadViewController implements
 	protected static final String CURRENT_PAGE_BUTTON = "current-page-button";
 	private static final String DURATION_FORMAT = "%d:%02d";
 
-	private TouchPadView padView;
+	private final TouchPadView padView;
 	private Pad pad;
 
-	private PadLockedListener padLockedListener;
-	private PadStatusListener padStatusListener;
-	private PadContentListener padContentListener;
-	private PadDurationListener padDurationListener;
-	private IPadPositionListener padPositionListener;
+	private final PadLockedListener padLockedListener;
+	private final PadStatusListener padStatusListener;
+	private final PadContentListener padContentListener;
+	private final PadDurationListener padDurationListener;
+	private final IPadPositionListener padPositionListener;
 
 	TouchPadViewController(TouchPadView padView) {
 		this.padView = padView;
@@ -174,20 +174,20 @@ public class TouchPadViewController extends AbstractPadViewController implements
 						String time = durationToString(duration);
 						padView.setTime(time);
 						padView.getPlayBar().setProgress(0);
+						padView.setCueInProgress(0);
 					} else {
+						// TODO: Duplicated code
 						// Play/Gesamtzeit anzeigen
 						TimeMode timeMode = pad.getPadSettings().getTimeMode();
 
 						if (timeMode == TimeMode.REST) {
 							Duration leftTime = duration.subtract(position);
-
 							padView.setTime("- " + durationToString(leftTime));
 						} else if (timeMode == TimeMode.PLAYED) {
 							padView.setTime(durationToString(position));
 						} else if (timeMode == TimeMode.BOTH) {
 							String time = durationToString(position);
 							String totalTime = durationToString(duration);
-
 							padView.setTime(time + "/" + totalTime);
 						}
 					}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioContent.java b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioContent.java
index b54079b8e799e072c95b4bbfec191f6a56bdc233..9ac9d7d961bdefb10bddbcffb81f65cf4e8d10b8 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioContent.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/pad/content/AudioContent.java
@@ -60,7 +60,7 @@ public class AudioContent extends PadContent implements Pauseable, Durationable,
 	}
 
 	@Override
-	public void play() {
+	public void play(boolean withFadeIn) {
 		getPad().setEof(false);
 		audioHandler.play();
 	}
@@ -134,8 +134,8 @@ public class AudioContent extends PadContent implements Pauseable, Durationable,
 
 	@Override
 	public void onFadeLevelChange(double level) {
-		Pad pad = getPad();
-		audioHandler.setVolume(level * VolumeManager.getInstance().computeVolume(pad));
+		final double newVolume = VolumeManager.getInstance().computeVolume(getPad()) * level;
+		audioHandler.setVolume(newVolume);
 	}
 
 	@Override
diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java
index a364fed1a5f5f6996c4885374d1b8acd70faa936..ffc4d9b1675b0b89e2fa755f28cc5dfa19c397d0 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadDurationListener.java
@@ -7,7 +7,7 @@ import javafx.util.Duration;
 
 public class PadDurationListener implements ChangeListener<Duration> {
 
-	private AbstractPadViewController controller;
+	private final AbstractPadViewController controller;
 
 	public PadDurationListener(AbstractPadViewController controller) {
 		this.controller = controller;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java
index 5326b5f914229d7a4e1b9499bb44c01234d00ecf..55d09a90259e837035805b1d3ea6d0c37519b6a4 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadPositionListener.java
@@ -2,9 +2,8 @@ package de.tobias.playpad.pad.listener;
 
 import de.tobias.playpad.PlayPadMain;
 import de.tobias.playpad.action.actions.CartAction;
-import de.tobias.playpad.design.ModernDesignHandler;
-import de.tobias.playpad.design.modern.ModernCartDesignHandler;
-import de.tobias.playpad.design.modern.ModernGlobalDesignHandler;
+import de.tobias.playpad.design.ModernDesignProvider;
+import de.tobias.playpad.design.modern.ModernWarningDesignHandler;
 import de.tobias.playpad.design.modern.model.ModernCartDesign;
 import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
 import de.tobias.playpad.pad.Pad;
@@ -21,7 +20,7 @@ import javafx.util.Duration;
 public class PadPositionListener implements Runnable, IPadPositionListener {
 
 	private Pad pad;
-	private AbstractPadViewController controller;
+	private final AbstractPadViewController controller;
 
 	public PadPositionListener(AbstractPadViewController controller) {
 		this.controller = controller;
@@ -79,8 +78,8 @@ public class PadPositionListener implements Runnable, IPadPositionListener {
 		// Label (Restlaufzeit)
 		controller.updateTimeLabel();
 
-		// Warning nur wenn kein Loop und nur wenn Play, da sonst schon anderer Zustand und Warning nicht mehr richtig Reseted
-		// wird
+		// Warning nur, wenn kein Loop und nur wenn Play, da sonst schon anderer Zustand und Warning nicht mehr richtig
+		// zurückgesetzt wird
 		if (!pad.getPadSettings().isLoop() && pad.getStatus() == PadStatus.PLAY) {
 			// Warning
 			Duration warning = pad.getPadSettings().getWarning();
@@ -91,6 +90,11 @@ public class PadPositionListener implements Runnable, IPadPositionListener {
 				startWarningThread();
 				send = true;
 			}
+
+			if (warning.toSeconds() < seconds && send) {
+				stopWaning();
+				send = false;
+			}
 		}
 	}
 
@@ -104,21 +108,16 @@ public class PadPositionListener implements Runnable, IPadPositionListener {
 	 */
 	@Override
 	public void run() {
-		PadSettings padSettings = pad.getPadSettings();
-		Duration warning = padSettings.getWarning();
+		final PadSettings padSettings = pad.getPadSettings();
+		final Duration warningDuration = padSettings.getWarning();
 
-		ModernGlobalDesign globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
-		final ModernDesignHandler modernDesign = PlayPadMain.getProgramInstance().getModernDesign();
+		final ModernGlobalDesign globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
+		final ModernCartDesign cartDesign = padSettings.getDesign();
 
-		if (padSettings.isCustomDesign()) {
-			ModernCartDesignHandler handler = modernDesign.cart();
-			ModernCartDesign design = pad.getPadSettings().getDesign();
+		final ModernDesignProvider modernDesign = PlayPadMain.getProgramInstance().getModernDesign();
 
-			handler.handleWarning(design, controller, warning, globalDesign);
-		} else {
-			ModernGlobalDesignHandler handler = modernDesign.global();
-			handler.handleWarning(globalDesign, controller, warning);
-		}
+		final ModernWarningDesignHandler handler = modernDesign.warning();
+		handler.handleWarning(globalDesign, cartDesign, controller, warningDuration);
 	}
 
 	private void startWarningThread() {
@@ -136,20 +135,10 @@ public class PadPositionListener implements Runnable, IPadPositionListener {
 			warningThread = null;
 		}
 
-		PadSettings padSettings = pad.getPadSettings();
-		final ModernDesignHandler modernDesign = PlayPadMain.getProgramInstance().getModernDesign();
+		final ModernDesignProvider modernDesign = PlayPadMain.getProgramInstance().getModernDesign();
 
-		if (padSettings.isCustomDesign()) {
-			ModernCartDesignHandler handler = modernDesign.cart();
-			ModernCartDesign design = pad.getPadSettings().getDesign();
-
-			handler.stopWarning(design, controller);
-		} else {
-			ModernGlobalDesignHandler handler = modernDesign.global();
-			ModernGlobalDesign globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
-
-			handler.stopWarning(globalDesign, controller);
-		}
+		final ModernWarningDesignHandler handler = modernDesign.warning();
+		handler.stopWarning(controller.getPad());
 		controller.getView().setStyle("");
 	}
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java
index 061cc894962680419298d76f8b326c825afb6770..826eab1bed1c29ae059fc8fda072dcfa077d3029 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/pad/listener/PadStatusListener.java
@@ -10,7 +10,7 @@ import javafx.beans.value.ObservableValue;
 
 public class PadStatusListener implements ChangeListener<PadStatus> {
 
-	private AbstractPadViewController controller;
+	private final AbstractPadViewController controller;
 
 	public PadStatusListener(AbstractPadViewController controller) {
 		this.controller = controller;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/project/ProjectJsonReader.java b/PlayWall/src/main/java/de/tobias/playpad/project/ProjectJsonReader.java
index 6f33e38af123e5c80346b484ffed63211fee6171..29030b5abfe80efd80615cf5ac5385cb5b371ca5 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/project/ProjectJsonReader.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/project/ProjectJsonReader.java
@@ -78,7 +78,6 @@ public class ProjectJsonReader {
 			ModernCartDesign design = readModernCartDesign(pad, object.getJSONObject("design"));
 			if (design != null) {
 				pad.getPadSettings().setDesign(design);
-				pad.getPadSettings().setCustomDesign(true); // TODO Sync
 			}
 		}
 
@@ -101,7 +100,11 @@ public class ProjectJsonReader {
 			ModernColor backgroundColor = ModernColor.valueOf(object.getString("background_color"));
 			ModernColor playColor = ModernColor.valueOf(object.getString("play_color"));
 
-			return new ModernCartDesign(pad, id, backgroundColor, playColor, ModernColor.RED2); // TODO Fix Cue In Color
+			return new ModernCartDesign.ModernCartDesignBuilder(pad, id)
+					.withBackgroundColor(backgroundColor, false)
+					.withPlayColor(playColor, false)
+					.withCueInColor(ModernColor.RED2, false)
+					.build(); // TODO Fix Cue In Color
 		}
 		return null;
 	}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/server/ServerImpl.java b/PlayWall/src/main/java/de/tobias/playpad/server/ServerImpl.java
index ca60c2c7bd416bf8da2e0b263172e9affec19ea1..6a7ae04733b411330de6ac4a6907dad0e586dacc 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/server/ServerImpl.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/server/ServerImpl.java
@@ -355,10 +355,10 @@ public class ServerImpl implements Server, ChangeListener<ConnectionState> {
 	}
 
 	private void loadStoredFile(Path path) throws IOException {
-		List<String> lines = Files.readAllLines(path);
-
-		JsonParser parser = new JsonParser();
-		List<JsonObject> commands = lines.stream().map(line -> (JsonObject) parser.parse(line)).collect(Collectors.toList());
+		final List<String> lines = Files.readAllLines(path);
+		final List<JsonObject> commands = lines.stream()
+				.map(line -> (JsonObject) JsonParser.parseString(line))
+				.collect(Collectors.toList());
 
 		CommandStore executor = (CommandStore) PlayPadPlugin.getCommandExecutorHandler().getCommandExecutor();
 		executor.setStoredCommands(path.getFileName().toString(), commands);
diff --git a/PlayWall/src/main/java/de/tobias/playpad/server/ServerSyncListener.java b/PlayWall/src/main/java/de/tobias/playpad/server/ServerSyncListener.java
index 5d94243b37a09505c791862f26b734a51f007dd2..7fc8eccda28a6b5d9f01b882cb7e3b8089e8ca81 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/server/ServerSyncListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/server/ServerSyncListener.java
@@ -45,9 +45,8 @@ public class ServerSyncListener extends WebSocketAdapter {
 	private static final int CONNECTION_CLOSED = 1005; // Login failed
 	private static final int DISCONNECTED = 1002; // Server closed
 
-	private ObjectProperty<ConnectionState> connectionStateProperty;
-
-	private Map<String, ServerListener> commands;
+	private final ObjectProperty<ConnectionState> connectionStateProperty;
+	private final Map<String, ServerListener> commands;
 
 	ServerSyncListener() {
 		commands = new HashMap<>();
@@ -103,7 +102,7 @@ public class ServerSyncListener extends WebSocketAdapter {
 	}
 
 	@Override
-	public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception {
+	public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) {
 		Logger.info("Disconnected: " + clientCloseFrame.getCloseReason());
 		if (clientCloseFrame.getCloseCode() == CONNECTION_CLOSED) {
 			connectionStateProperty.set(ConnectionState.CONNECTION_LOST);
@@ -115,9 +114,8 @@ public class ServerSyncListener extends WebSocketAdapter {
 	}
 
 	@Override
-	public void onTextMessage(WebSocket websocket, String text) throws Exception {
-		JsonParser parser = new JsonParser();
-		JsonElement element = parser.parse(text);
+	public void onTextMessage(WebSocket websocket, String text) {
+		final JsonElement element = JsonParser.parseString(text);
 		if (element instanceof JsonObject) {
 			JsonObject json = (JsonObject) element;
 			String cmd = json.get("cmd").getAsString();
diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItem.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItem.java
index 7bb7597bec88d5a0f72cfae7fb6d7f75e62c5ebe..af4bbaf11df48d35020b3c4d6b9ded20385b0fae 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItem.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/CartTriggerItem.java
@@ -5,30 +5,30 @@ import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.PadStatus;
 import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.project.Project;
+import de.tobias.playpad.tigger.LocalPadTrigger;
 import de.tobias.playpad.tigger.TriggerItem;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
 import org.dom4j.Element;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
-public class CartTriggerItem extends TriggerItem {
+public class CartTriggerItem extends TriggerItem implements LocalPadTrigger {
 
 	private List<UUID> uuids;
 	private boolean allCarts;
 	private PadStatus newStatus; // Only Play, Pause, Stop
 
-	private String type;
+	private final String type;
 
 	CartTriggerItem(String type) {
-		super();
 		this.type = type;
-		newStatus = PadStatus.PLAY;
-		allCarts = false;
-		uuids = new UniqList<>();
+		this.newStatus = PadStatus.PLAY;
+		this.allCarts = false;
+		this.uuids = new UniqList<>();
 	}
 
+	@Override
 	public List<UUID> getCarts() {
 		return uuids;
 	}
@@ -64,11 +64,9 @@ public class CartTriggerItem extends TriggerItem {
 			}
 		} else {
 			for (UUID uuid : uuids) {
-				if (!uuid.equals(source.getUuid())) {
-					Pad pad = project.getPad(uuid);
-					if (pad != null)
-						pad.setStatus(newStatus);
-				}
+				Pad pad = project.getPad(uuid);
+				if (pad != null)
+					pad.setStatus(newStatus);
 			}
 		}
 	}
@@ -77,7 +75,7 @@ public class CartTriggerItem extends TriggerItem {
 	public TriggerItem copy() {
 		CartTriggerItem clone = new CartTriggerItem(getType());
 
-		clone.uuids = new ArrayList<>();
+		clone.uuids = new UniqList<>();
 		clone.uuids.addAll(uuids);
 		clone.allCarts = allCarts;
 		clone.newStatus = newStatus;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItem.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItem.java
index 667c4aa90b0cb045c1655a0ccea97a897061979a..88fb85bc6d345ff4b1df884d2c7c42152a949f8d 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItem.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerItem.java
@@ -21,7 +21,7 @@ public class VolumeTriggerItem extends TriggerItem {
 	private Duration duration = new Duration(2000);
 	private List<UUID> uuids;
 
-	private String type;
+	private final String type;
 
 	VolumeTriggerItem(String type) {
 		super();
@@ -58,12 +58,18 @@ public class VolumeTriggerItem extends TriggerItem {
 	public void performAction(Pad pad, Project project, IMainViewController controller, Profile profile) {
 		uuids.stream().map(project::getPad)
 				.filter(i -> i.getContent() instanceof Fadeable)
-				.forEach(destination -> {
-			Fadeable fadeable = (Fadeable) destination.getContent();
-
-			final double start = VolumeManager.getInstance().computeVolume(destination);
-			fadeable.fade(start, volume, duration, null);
-		});
+				.forEach(targetPad -> {
+					final Fadeable fadeable = (Fadeable) targetPad.getContent();
+
+					// Use pad * last VolumeTrigger
+					final double start = VolumeManager.getInstance().computeVolume(targetPad) * VolumeTriggerVolumeFilter.getInstance().getVolume(pad);
+					// Use pad * target VolumeTrigger
+					final double destination = VolumeManager.getInstance().computeVolume(targetPad) * volume;
+					// Update VolumeTrigger
+					VolumeTriggerVolumeFilter.getInstance().setVolume(pad, volume);
+
+					fadeable.fade(start, destination, duration, null);
+				});
 	}
 
 	@Override
diff --git a/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerVolumeFilter.java b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerVolumeFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..83a730951c8d0f0a5183237ecf19f8837f4d50b6
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/trigger/VolumeTriggerVolumeFilter.java
@@ -0,0 +1,52 @@
+package de.tobias.playpad.trigger;
+
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.PadStatus;
+import de.tobias.playpad.plugin.PadListener;
+import de.tobias.playpad.volume.VolumeFilter;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class VolumeTriggerVolumeFilter implements VolumeFilter, PadListener {
+
+	private static VolumeTriggerVolumeFilter instance;
+
+	public static VolumeTriggerVolumeFilter getInstance() {
+		if (instance == null) {
+			instance = new VolumeTriggerVolumeFilter();
+		}
+		return instance;
+	}
+
+	private VolumeTriggerVolumeFilter() {
+		// nothing to do
+	}
+
+	private final Map<UUID, Double> volumes = new HashMap<>();
+
+	@Override
+	public double getVolume(Pad pad) {
+		return volumes.getOrDefault(pad.getUuid(), 1.0);
+	}
+
+	public void setVolume(Pad pad, double newVolume) {
+		volumes.put(pad.getUuid(), newVolume);
+	}
+
+	// Pad Listener
+
+
+	@Override
+	public void onNameChanged(Pad pad, String oldValue, String newValue) {
+		// nothing to do
+	}
+
+	@Override
+	public void onStatusChange(Pad pad, PadStatus oldValue, PadStatus newValue) {
+		if (oldValue == PadStatus.READY && newValue == PadStatus.PLAY) {
+			volumes.remove(pad.getUuid());
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/update/VersionUpdater.java b/PlayWall/src/main/java/de/tobias/playpad/update/VersionUpdater.java
index b5f7c5be9793d58da416f0c68334c94194d36afe..dbdd1e078924b043429b4edcd07256d607cce639 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/update/VersionUpdater.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/update/VersionUpdater.java
@@ -1,13 +1,91 @@
 package de.tobias.playpad.update;
 
+import de.thecodelabs.logger.Logger;
 import de.thecodelabs.utils.application.App;
+import de.thecodelabs.utils.application.container.PathType;
 import de.thecodelabs.utils.application.update.UpdateService;
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.XMLWriter;
 
-public class VersionUpdater implements UpdateService {
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.UUID;
 
+public class VersionUpdater implements UpdateService {
 	@Override
 	public void update(App app, long oldVersion, long newVersion) {
+		try {
+			if (oldVersion < 44) {
+				updateTo44(app);
+			}
+		} catch (Exception e) {
+			Logger.error(e);
+		}
+	}
+
+	private void updateTo44(App app) throws DocumentException, IOException {
+		Logger.debug("Updating to app version 44...");
 
+		SAXReader reader = new SAXReader();
+
+		Document projectsDocument = reader.read(Files.newInputStream(app.getPath(PathType.CONFIGURATION, "Projects.xml")));
+		for (Element element : projectsDocument.getRootElement().elements("Project")) {
+			final String projectName = element.attributeValue("name");
+			final UUID projectUUID = UUID.fromString(element.attributeValue("uuid"));
+
+			Logger.debug(MessageFormat.format("Updating project \"{0}\" (id: {1})...", projectName, projectUUID));
+			Path projectPath = app.getPath(PathType.DOCUMENTS, projectUUID.toString() + ".xml");
+			final Document migratedProject = updateProject(Files.newInputStream(projectPath));
+
+			XMLWriter writer = new XMLWriter(Files.newOutputStream(projectPath), OutputFormat.createPrettyPrint());
+			writer.write(migratedProject);
+			writer.close();
+		}
 	}
 
+	public static Document updateProject(InputStream projectInputStream) throws DocumentException {
+		SAXReader reader = new SAXReader();
+
+		Document projectDocument = reader.read(projectInputStream);
+		Element rootProjectElement = projectDocument.getRootElement();
+
+		for (Element page : rootProjectElement.elements()) {
+			for (Element pad : page.elements()) {
+				final Element settings = pad.element("Settings");
+				if (settings == null) {
+					continue;
+				}
+
+				final Element designElement = settings.element("Design");
+				if (designElement == null) {
+					continue;
+				}
+
+				final Attribute customFlag = designElement.attribute("custom");
+				if (customFlag == null) {
+					continue;
+				}
+
+				designElement.remove(customFlag);
+
+				if (customFlag.getValue().equals("false")) {
+					continue;
+				}
+
+				designElement.addElement("EnableCustomBackgroundColor").addText("true");
+				designElement.addElement("EnableCustomPlayColor").addText("true");
+				designElement.addElement("EnableCustomCueInColor").addText("true");
+			}
+		}
+
+		return projectDocument;
+	}
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/PseudoClasses.java b/PlayWall/src/main/java/de/tobias/playpad/view/PseudoClasses.java
index 3af53dd3a98de27a9bde11cfeceb946b627c57f1..1a62b1c17ad70d65e8e2a349bc7b3ad55cfab77a 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/view/PseudoClasses.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/PseudoClasses.java
@@ -15,6 +15,4 @@ public class PseudoClasses {
 	public static final PseudoClass DRAG_CLASS = PseudoClass.getPseudoClass("drag");
 
 	public static final PseudoClass DEACTIVATED_CLASS = PseudoClass.getPseudoClass("deactivated");
-
-
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/AbstractActionViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/AbstractActionViewController.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f378756b825dd501e3eb97fbabb9bbe99341939
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/AbstractActionViewController.java
@@ -0,0 +1,8 @@
+package de.tobias.playpad.viewcontroller.actions;
+
+import de.thecodelabs.midi.action.Action;
+import de.thecodelabs.utils.ui.NVC;
+
+public abstract class AbstractActionViewController extends NVC {
+	public abstract void setCartAction(Action action);
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionTypeViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionTypeViewController.java
index 8573b914f06060b1e035a45d13907bb55378f629..ee1dda9212e66fe9acc5ab4a1916a74445d94ab1 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionTypeViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionTypeViewController.java
@@ -17,7 +17,9 @@ import javafx.scene.control.ToggleGroup;
 import javafx.scene.layout.*;
 import org.controlsfx.control.SegmentedButton;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Diese View ist die Basis für die Einstellunge für eine CartAction. Dabei enthällt diese View ein Grid aus Buttons (Carts), eine
@@ -36,17 +38,24 @@ public class CartActionTypeViewController extends NVC {
 	@FXML
 	private VBox cartActionContainer;
 
-	private CartActionViewController cartActionViewController;
+	private final AbstractActionViewController actionViewController;
+	private final String actionType;
 
-	private Mapping mapping;
-	private IMappingTabViewController parentController;
+	private final Mapping mapping;
+	private final IMappingTabViewController parentController;
 
-	public CartActionTypeViewController(Mapping mapping, IMappingTabViewController parentController) {
+
+	public CartActionTypeViewController(Mapping mapping, IMappingTabViewController parentController, String actionType) {
+		this(mapping, parentController, actionType, null);
+	}
+
+	public CartActionTypeViewController(Mapping mapping, IMappingTabViewController parentController, String actionType, AbstractActionViewController actionViewController) {
 		load("view/actions", "CartActions", Localization.getBundle());
 		this.mapping = mapping;
 		this.parentController = parentController;
 
-		cartActionViewController = new CartActionViewController();
+		this.actionType = actionType;
+		this.actionViewController = actionViewController;
 
 		Project currentProject = PlayPadMain.getProgramInstance().getCurrentProject();
 		ProjectSettings settings = currentProject.getSettings();
@@ -106,15 +115,21 @@ public class CartActionTypeViewController extends NVC {
 			int currentY = data[1];
 
 			try {
-				List<Action> cartActions = mapping.getActionsForType(CartAction.TYPE);
-				for (Action action : cartActions) {
-					if (CartAction.getX(action) == currentX && CartAction.getY(action) == currentY) {
-						cartActionContainer.getChildren().setAll(cartActionViewController.getParent());
-						cartActionContainer.setVisible(true);
-						cartActionViewController.setCartAction(action);
-						parentController.showMapperFor(action);
-					}
+				final List<Action> cartActions = mapping.getActionsForType(actionType).stream()
+						.filter(action -> CartAction.getX(action) == currentX && CartAction.getY(action) == currentY)
+						.collect(Collectors.toList());
+
+				if (cartActions.size() > 1) {
+					throw new IllegalArgumentException("Only one action allowed per pad. Currently " + cartActions.size() + " are registered for pad coordinate " + Arrays.toString(data));
+				}
+
+				final Action action = cartActions.get(0);
+				if (actionViewController != null) {
+					cartActionContainer.getChildren().setAll(actionViewController.getParent());
+					cartActionContainer.setVisible(true);
+					actionViewController.setCartAction(action);
 				}
+				parentController.showMapperFor(action);
 			} catch (NoSuchComponentException e) {
 				Logger.error(e);
 			}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionViewController.java
index b43977c2420ae78ac8bb1ca86e4585a88e847088..e3fe4664112f86babef0a24a3ce16e1f4c1c2159 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/actions/CartActionViewController.java
@@ -1,7 +1,6 @@
 package de.tobias.playpad.viewcontroller.actions;
 
 import de.thecodelabs.midi.action.Action;
-import de.thecodelabs.utils.ui.NVC;
 import de.thecodelabs.utils.util.Localization;
 import de.tobias.playpad.Strings;
 import de.tobias.playpad.action.actions.CartAction;
@@ -13,7 +12,7 @@ import javafx.scene.control.ComboBox;
 import javafx.scene.layout.Priority;
 import javafx.scene.layout.VBox;
 
-public class CartActionViewController extends NVC {
+public class CartActionViewController extends AbstractActionViewController {
 
 	@FXML
 	private ComboBox<CartActionMode> controlMode;
@@ -41,6 +40,7 @@ public class CartActionViewController extends NVC {
 		VBox.setVgrow(rootContainer, Priority.ALWAYS);
 	}
 
+	@Override
 	public void setCartAction(Action action) {
 		this.action = action;
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/cell/LocalizeCell.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/cell/LocalizeCell.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce138035282722a35ee8f74f2774200f0a079a5a
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/cell/LocalizeCell.java
@@ -0,0 +1,23 @@
+package de.tobias.playpad.viewcontroller.cell;
+
+import de.thecodelabs.utils.util.Localization;
+import javafx.scene.control.ListCell;
+
+public class LocalizeCell extends ListCell<String> {
+
+	private final String baseName;
+
+	public LocalizeCell(String baseName) {
+		this.baseName = baseName;
+	}
+
+	@Override
+	protected void updateItem(String item, boolean empty) {
+		super.updateItem(item, empty);
+		if (!empty) {
+			setText(Localization.getString(baseName + item));
+		} else {
+			setText("");
+		}
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/design/ModernCartDesignViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/design/ModernCartDesignViewController.java
index fbf39d6b0e6d113d1b5d08bcf1852194f17f2e02..57d922e1253e9bcad2fb833cff26edcc37d6f416 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/design/ModernCartDesignViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/design/ModernCartDesignViewController.java
@@ -17,6 +17,13 @@ import java.util.function.Consumer;
 
 public class ModernCartDesignViewController extends NVC implements IColorButton {
 
+	@FXML
+	private CheckBox backgroundColorCheckbox;
+	@FXML
+	private CheckBox playColorCheckbox;
+	@FXML
+	private CheckBox cueInColorCheckbox;
+
 	@FXML
 	private Button backgroundColorButton;
 	@FXML
@@ -29,25 +36,49 @@ public class ModernCartDesignViewController extends NVC implements IColorButton
 	@FXML
 	private Button resetButton;
 
-	private ModernCartDesign design;
+	private final ModernCartDesign design;
 
 	private PopOver colorChooser;
 
-	public ModernCartDesignViewController(ModernCartDesign layout) {
+	public ModernCartDesignViewController(ModernCartDesign design) {
 		load("view/option/layout", "ModernLayoutCart", Localization.getBundle());
 
-		this.design = layout;
-		setLayout();
+		this.design = design;
+		setDesign();
 	}
 
-	private void setLayout() {
+	private void setDesign() {
 		backgroundColorButton.setStyle(getLinearGradientCss(design.getBackgroundColor()));
 		playColorButton.setStyle(getLinearGradientCss(design.getPlayColor()));
 		cueInColorButton.setStyle(getLinearGradientCss(design.getCueInColor()));
+
+		backgroundColorCheckbox.setSelected(design.isEnableCustomBackgroundColor());
+		playColorCheckbox.setSelected(design.isEnableCustomPlayColor());
+		cueInColorCheckbox.setSelected(design.isEnableCustomCueInColor());
 	}
 
 	@Override
 	public void init() {
+		backgroundColorCheckbox.selectedProperty().addListener((observable, oldValue, newValue) ->
+		{
+			design.setEnableCustomBackgroundColor(newValue);
+			backgroundColorButton.setDisable(!newValue);
+		});
+		playColorCheckbox.selectedProperty().addListener((observable, oldValue, newValue) ->
+		{
+			design.setEnableCustomPlayColor(newValue);
+			playColorButton.setDisable(!newValue);
+		});
+		cueInColorCheckbox.selectedProperty().addListener((observable, oldValue, newValue) ->
+		{
+			design.setEnableCustomCueInColor(newValue);
+			cueInColorButton.setDisable(!newValue);
+		});
+
+		backgroundColorButton.setDisable(true);
+		playColorButton.setDisable(true);
+		cueInColorButton.setDisable(true);
+
 		addIconToButton(backgroundColorButton);
 		addIconToButton(playColorButton);
 		addIconToButton(cueInColorButton);
@@ -56,22 +87,22 @@ public class ModernCartDesignViewController extends NVC implements IColorButton
 	@FXML
 	private void resetButtonHandler(ActionEvent event) {
 		design.reset();
-		setLayout();
+		setDesign();
 	}
 
 	@FXML
 	private void backgroundColorButtonHandler(ActionEvent event) {
-		colorChooser(backgroundColorButton, design.getBackgroundColor(), color -> design.setBackgroundColor(color));
+		colorChooser(backgroundColorButton, design.getBackgroundColor(), design::setBackgroundColor);
 	}
 
 	@FXML
 	private void playColorButtonHandler(ActionEvent event) {
-		colorChooser(playColorButton, design.getPlayColor(), color -> design.setPlayColor(color));
+		colorChooser(playColorButton, design.getPlayColor(), design::setPlayColor);
 	}
 
 	@FXML
 	private void cueInColorButtonHandler(ActionEvent event) {
-		colorChooser(cueInColorButton, design.getPlayColor(), color -> design.setCueInColor(color));
+		colorChooser(cueInColorButton, design.getPlayColor(), design::setCueInColor);
 	}
 
 	private void colorChooser(Button anchorNode, ModernColor startColor, Consumer<ModernColor> onFinish) {
@@ -101,5 +132,4 @@ public class ModernCartDesignViewController extends NVC implements IColorButton
 			return "-fx-background-color: " + color.linearGradient() + ";";
 		}
 	}
-
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/dialog/ModernPluginViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/dialog/ModernPluginViewController.java
index 63e4ba8a4d673f84251840133cde64e92b98eab3..404bd0b40960162ecec8d5fd7a4f0dad526c2aa7 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/dialog/ModernPluginViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/dialog/ModernPluginViewController.java
@@ -119,10 +119,9 @@ public class ModernPluginViewController extends NVC implements ChangeListener<Mo
 		pluginList.getSelectionModel().selectedItemProperty().addListener(this);
 
 		searchField.textProperty().addListener((observable, oldValue, newValue) -> {
-			if(newValue == null || newValue.length() == 0) {
+			if (newValue == null || newValue.length() == 0) {
 				filteredData.setPredicate(s -> true);
-			}
-			else {
+			} else {
 				filteredData.setPredicate(p -> p.getDisplayName().contains(newValue));
 			}
 		});
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/AutosaveRunner.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/AutosaveRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..798e1d6bfef655758abb566b1fda26b180c73cf9
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/AutosaveRunner.java
@@ -0,0 +1,49 @@
+package de.tobias.playpad.viewcontroller.main;
+
+import de.thecodelabs.logger.Logger;
+import de.tobias.playpad.PlayPadPlugin;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class AutosaveRunner implements Runnable {
+	private final MainViewController mainViewController;
+
+	public AutosaveRunner(MainViewController mainViewController) {
+		this.mainViewController = mainViewController;
+	}
+
+	@Override
+	public void run() {
+		long lastSaveTime = System.currentTimeMillis();
+		final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+		while (!Thread.interrupted()) {
+			final long currentMillis = System.currentTimeMillis();
+
+			// autosave interval may be changed by user in global settings, therefore the current setting needs to be fetched every time
+			if (currentMillis > lastSaveTime + getAutosaveIntervalInMillis()) {
+				lastSaveTime = currentMillis;
+
+				if (PlayPadPlugin.getInstance().getGlobalSettings().isEnableAutosave()) {
+					Logger.debug("Performing autosave...");
+					mainViewController.save();
+
+					long nextSaveTime = currentMillis + getAutosaveIntervalInMillis();
+					Logger.debug("Autosave done. Next predicted autosave: " + dateFormat.format(new Date(nextSaveTime)));
+				}
+			}
+
+			try {
+				//noinspection BusyWait
+				Thread.sleep(10 * 1000L);
+			} catch (InterruptedException e) {
+				Thread.currentThread().interrupt();
+			}
+		}
+	}
+
+	private long getAutosaveIntervalInMillis() {
+		return PlayPadPlugin.getInstance().getGlobalSettings().getAutosaveIntervalInMinutes() * 60 * 1000L;
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java
index ec8f51a3dfc669a93d119794e71e1f9b2d29ee48..802ba9ea1a45b76ff9247a1c5797a77e02142e0d 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/MainViewController.java
@@ -28,6 +28,7 @@ import de.tobias.playpad.plugin.MainWindowListener;
 import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.profile.ProfileListener;
 import de.tobias.playpad.profile.ProfileSettings;
+import de.tobias.playpad.profile.ref.ProfileReferenceManager;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.project.ProjectSettings;
 import de.tobias.playpad.project.page.PadIndex;
@@ -68,6 +69,7 @@ import javafx.stage.Screen;
 import javafx.stage.Stage;
 
 import javax.sound.midi.MidiUnavailableException;
+import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
@@ -111,6 +113,8 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 	private InvalidationListener projectTitleListener;
 	private InvalidationListener pagesListener;
 
+	private Thread autosaveThread;
+
 	public MainViewController(Consumer<NVC> onFinish) {
 		load("view/main", "MainView", Localization.getBundle(), e ->
 		{
@@ -208,6 +212,9 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 		} catch (Exception e) {
 			Logger.error(e);
 		}
+
+		autosaveThread = new Thread(new AutosaveRunner(this));
+		autosaveThread.start();
 	}
 
 	@Override
@@ -308,8 +315,8 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 			ProfileSettings profileSettings = Profile.currentProfile().getProfileSettings();
 			GlobalSettings globalSettings = PlayPadPlugin.getInstance().getGlobalSettings();
 
-			// Frag den Nutzer ob das Programm wirdklich geschlossen werden sol
-			// wenn ein Pad noch im Status Play ist
+			// Frag den Nutzer ob das Programm wirdklich geschlossen werden soll,
+			// falls noch ein Pad im Status Play ist
 			if (openProject.getActivePlayers() > 0 && globalSettings.isLiveMode()) {
 				Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
 				alert.setContentText(Localization.getString(Strings.UI_WINDOW_MAIN_CLOSE_REQUEST));
@@ -343,7 +350,7 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 				}
 			}
 
-			// Save Config - Its unabhängig vom Dialog, da es auch an anderen Stellen schon gespeichert wird
+			// Save Config - Ist unabhängig vom Dialog, da es auch an anderen Stellen schon gespeichert wird
 			try {
 				if (Profile.currentProfile() != null)
 					Profile.currentProfile().save();
@@ -362,6 +369,11 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 					Logger.error(e);
 				}
 			}
+
+			if (autosaveThread != null && autosaveThread.isAlive()) {
+				Logger.debug("Stopping autosave thread");
+				autosaveThread.interrupt();
+			}
 		}
 		Platform.exit();
 		return true;
@@ -456,6 +468,8 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 		}
 		loadUserCss();
 
+		PlayPadPlugin.getInstance().getMainViewListeners().forEach(listener -> listener.onCurrentPageChanged(page));
+
 		return true;
 	}
 
@@ -788,4 +802,19 @@ public class MainViewController extends NVC implements IMainViewController, Noti
 		PlayPadPlugin.getInstance().getMainViewListeners().forEach(MainWindowListener::loadMenuKeyBinding);
 	}
 
+	@Override
+	public void save() {
+		try {
+			ProjectReferenceManager.saveProjects();
+			ProjectReferenceManager.saveSingleProject(openProject);
+			ProfileReferenceManager.saveProfiles();
+			Profile.currentProfile().save();
+			PlayPadPlugin.getInstance().getGlobalSettings().save();
+
+			notify(Localization.getString(Strings.STANDARD_FILE_SAVE), PlayPadMain.NOTIFICATION_DISPLAY_TIME);
+		} catch (IOException e) {
+			showError(Localization.getString(Strings.ERROR_PROJECT_SAVE));
+			Logger.error(e);
+		}
+	}
 }
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
index 00748a4a0349732f84622714271cae8f4474ecfe..43127d5e16bea237edac3fbb81e3268a8d5d23c6 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/main/listener/LockedListener.java
@@ -10,9 +10,8 @@ import javafx.scene.control.Label;
 
 public class LockedListener implements ChangeListener<Boolean> {
 
-	private IMainViewController mainViewController;
-
-	private Label lockedLabel;
+	private final IMainViewController mainViewController;
+	private final Label lockedLabel;
 
 	public LockedListener(IMainViewController mainViewController) {
 		this.mainViewController = mainViewController;
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/feedback/SingleFeedbackViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/feedback/SingleFeedbackViewController.java
index a949622480873dcea5e15793d279d61a25bcf05f..66bc7372d9f342ba83f7cee5dd48334ff89bd200 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/feedback/SingleFeedbackViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/feedback/SingleFeedbackViewController.java
@@ -10,9 +10,6 @@ import de.thecodelabs.utils.ui.NVC;
 import de.thecodelabs.utils.ui.icon.FontIcon;
 import de.thecodelabs.utils.util.ColorUtils;
 import de.thecodelabs.utils.util.Localization;
-import de.tobias.playpad.action.feedback.LightMode;
-import de.tobias.playpad.profile.Profile;
-import de.tobias.playpad.profile.ProfileSettings;
 import de.tobias.playpad.view.FeedbackColorPickerView;
 import de.tobias.playpad.viewcontroller.design.IColorButton;
 import javafx.event.ActionEvent;
@@ -43,9 +40,9 @@ public class SingleFeedbackViewController extends NVC implements IColorButton {
 
 	private PopOver colorChooser;
 
-	private FeedbackColor[] colors;
-	private Feedback feedback;
-	private Action action;
+	private final FeedbackColor[] colors;
+	private final Feedback feedback;
+	private final Action action;
 
 	public SingleFeedbackViewController(Feedback feedback, FeedbackType type, FeedbackValue[] values, Action action) {
 		load("view/option/feedback", "SingleFeedback", Localization.getBundle());
@@ -98,11 +95,6 @@ public class SingleFeedbackViewController extends NVC implements IColorButton {
 			final FeedbackColor feedbackColor = getFeedbackColor(feedback);
 
 			List<? extends FeedbackColor> selectableColors = Arrays.asList(colors);
-			if (colors instanceof LightMode.ILightMode[]) {
-				ProfileSettings profileSettings = Profile.currentProfile().getProfileSettings();
-				selectableColors = LightMode.filter((LightMode.ILightMode[]) colors, profileSettings.getLightMode());
-			}
-
 			FeedbackColorPickerView colorView = new FeedbackColorPickerView(feedbackColor, selectableColors, item ->
 			{
 				colorChooser.hide();
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/global/GlobalSettingsViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/global/GlobalSettingsViewController.java
index e29db503f32fbff22d8f33dcf471b8423c9238af..4c862a80dbd9867a2094d4a9245700f5f95c9aac 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/global/GlobalSettingsViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/global/GlobalSettingsViewController.java
@@ -73,7 +73,7 @@ public class GlobalSettingsViewController extends NVC implements IGlobalSettings
 		stage.getIcons().add(PlayPadPlugin.getInstance().getIcon());
 
 		stage.setMinWidth(715);
-		stage.setMinHeight(700);
+		stage.setMinHeight(800);
 		stage.setTitle(Localization.getString(Strings.UI_WINDOW_GLOBAL_SETTINGS_TITLE));
 
 		PlayPadPlugin.styleable().applyStyle(stage);
@@ -111,9 +111,7 @@ public class GlobalSettingsViewController extends NVC implements IGlobalSettings
 	// Button Listener
 	@FXML
 	private void finishButtonHandler(ActionEvent event) {
-		if (onFinish()) {
-			getStageContainer().ifPresent(NVCStage::close);
-		}
+		getStageContainer().ifPresent(NVCStage::close);
 	}
 
 	/**
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/DesignPadTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/DesignPadTabViewController.java
index 9350b63841d763894290086376d135a3df8b02cf..c08a6a30d3ee2a2822d4ad105b73eb1dc09941c2 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/DesignPadTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/DesignPadTabViewController.java
@@ -6,23 +6,18 @@ import de.tobias.playpad.PlayPadPlugin;
 import de.tobias.playpad.Strings;
 import de.tobias.playpad.design.modern.model.ModernCartDesign;
 import de.tobias.playpad.pad.Pad;
-import de.tobias.playpad.pad.PadSettings;
-import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.viewcontroller.PadSettingsTabViewController;
 import de.tobias.playpad.viewcontroller.design.ModernCartDesignViewController;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
 import javafx.fxml.FXML;
-import javafx.scene.control.CheckBox;
 import javafx.scene.layout.VBox;
 
 public class DesignPadTabViewController extends PadSettingsTabViewController {
 
 	@FXML
 	private VBox layoutContainer;
-	@FXML
-	private CheckBox enableLayoutCheckBox;
 
-	private Pad pad;
+	private final Pad pad;
 
 	DesignPadTabViewController(Pad pad) {
 		load("view/option/pad", "LayoutTab", Localization.getBundle());
@@ -31,26 +26,6 @@ public class DesignPadTabViewController extends PadSettingsTabViewController {
 
 	@Override
 	public void init() {
-		enableLayoutCheckBox.selectedProperty().addListener((a, b, c) ->
-		{
-			PadSettings padSettings = pad.getPadSettings();
-			if (c && !padSettings.isCustomDesign()) {
-				try {
-					padSettings.setCustomDesign(true);
-
-					ModernCartDesign layout = padSettings.getDesign();
-					layout.copyGlobalLayout(Profile.currentProfile().getProfileSettings().getDesign());
-
-					setLayoutViewController(pad);
-				} catch (Exception e) {
-					showErrorMessage(Localization.getString(Strings.ERROR_STANDARD_GEN, e.getLocalizedMessage()));
-					Logger.error(e);
-				}
-			} else if (!c && padSettings.isCustomDesign()) {
-				padSettings.setCustomDesign(false);
-				setLayoutViewController(null);
-			}
-		});
 	}
 
 	@Override
@@ -60,12 +35,7 @@ public class DesignPadTabViewController extends PadSettingsTabViewController {
 
 	@Override
 	public void loadSettings(Pad pad) {
-		PadSettings padSettings = pad.getPadSettings();
-
-		enableLayoutCheckBox.setSelected(padSettings.isCustomDesign());
-		if (padSettings.isCustomDesign()) {
-			setLayoutViewController(pad);
-		}
+		setLayoutViewController(pad);
 	}
 
 	private void setLayoutViewController(Pad pad) {
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlayerPadTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlayerPadTabViewController.java
index 11d394f681a0bb2f852d0907cffd60e927012d4b..ed283f8db6cf955021cf3ab5c50c4c0cb024d2af 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlayerPadTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlayerPadTabViewController.java
@@ -17,6 +17,8 @@ import javafx.util.Duration;
 
 public class PlayerPadTabViewController extends PadSettingsTabViewController {
 
+	@FXML
+	private CheckBox playOverlayEnableCheckBox;
 	@FXML
 	private CheckBox customFadeCheckBox;
 	@FXML
@@ -48,6 +50,8 @@ public class PlayerPadTabViewController extends PadSettingsTabViewController {
 		warningFeedbackViewController = WarningFeedbackViewController.newViewControllerForPad();
 		warningFeedbackContainer.getChildren().add(warningFeedbackViewController.getParent());
 
+		playOverlayEnableCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> pad.getPadSettings().setPlayOverlay(newValue));
+
 		customFadeCheckBox.selectedProperty().addListener((a, b, c) ->
 		{
 			fadeContainer.setDisable(!c);
@@ -103,6 +107,8 @@ public class PlayerPadTabViewController extends PadSettingsTabViewController {
 	public void loadSettings(Pad pad) {
 		PadSettings padSettings = pad.getPadSettings();
 
+		playOverlayEnableCheckBox.setSelected(padSettings.isPlayOverlay());
+
 		if (padSettings.isCustomFade())
 			fadeViewController.setFadeSettings(padSettings.getFade());
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java
index 0a80e87d3169c61af24b9a8fdaf0f233f6cf2979..74de2555dab74e6fc625506f4a848f7b3547c663 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/PlaylistTabViewController.java
@@ -9,6 +9,7 @@ import de.tobias.playpad.Strings;
 import de.tobias.playpad.layout.desktop.listener.PadNewContentListener;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.content.PadContentFactory;
+import de.tobias.playpad.pad.content.PadContentPlaylistFactory;
 import de.tobias.playpad.pad.content.PadContentRegistry;
 import de.tobias.playpad.pad.content.Playlistable;
 import de.tobias.playpad.pad.mediapath.MediaPath;
@@ -16,10 +17,9 @@ import de.tobias.playpad.viewcontroller.PadSettingsTabViewController;
 import javafx.beans.binding.Bindings;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
-import javafx.scene.control.Button;
-import javafx.scene.control.Label;
-import javafx.scene.control.ListCell;
-import javafx.scene.control.ListView;
+import javafx.scene.Node;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
 
 import java.io.File;
 import java.util.Collections;
@@ -28,10 +28,15 @@ import java.util.List;
 public class PlaylistTabViewController extends PadSettingsTabViewController {
 
 	@FXML
-	private ListView<MediaPath> mediaPathListView;
+	private CheckBox shuffleCheckbox;
+	@FXML
+	private CheckBox autoNextCheckbox;
 
 	@FXML
 	private Button addButton;
+
+	@FXML
+	private ListView<MediaPath> mediaPathListView;
 	@FXML
 	private Button upButton;
 	@FXML
@@ -44,6 +49,9 @@ public class PlaylistTabViewController extends PadSettingsTabViewController {
 	@FXML
 	private Button showFileButton;
 
+	@FXML
+	private VBox customItemView;
+
 	private final Pad pad;
 
 	public PlaylistTabViewController(Pad pad) {
@@ -86,6 +94,14 @@ public class PlaylistTabViewController extends PadSettingsTabViewController {
 			} else {
 				pathLabel.setText(Localization.getString("padSettings.gen.label.media.empty"));
 			}
+
+			customItemView.getChildren().clear();
+
+			final PadContentFactory factory = PlayPadPlugin.getRegistries().getPadContents().getFactory(pad.getContentType());
+			if (factory instanceof PadContentPlaylistFactory) {
+				final Node customPlaylistItemView = ((PadContentPlaylistFactory) factory).getCustomPlaylistItemView(pad, newValue);
+				customItemView.getChildren().add(customPlaylistItemView);
+			}
 		});
 	}
 
@@ -108,12 +124,14 @@ public class PlaylistTabViewController extends PadSettingsTabViewController {
 
 	@Override
 	public void loadSettings(Pad pad) {
-		// Not implemented
+		shuffleCheckbox.setSelected((Boolean) pad.getPadSettings().getCustomSettings().getOrDefault(Playlistable.SHUFFLE_SETTINGS_KEY, false));
+		autoNextCheckbox.setSelected((Boolean) pad.getPadSettings().getCustomSettings().getOrDefault(Playlistable.AUTO_NEXT_SETTINGS_KEY, true));
 	}
 
 	@Override
 	public void saveSettings(Pad pad) {
-		// Not implemented
+		pad.getPadSettings().getCustomSettings().put(Playlistable.SHUFFLE_SETTINGS_KEY, shuffleCheckbox.isSelected());
+		pad.getPadSettings().getCustomSettings().put(Playlistable.AUTO_NEXT_SETTINGS_KEY, autoNextCheckbox.isSelected());
 	}
 
 	@FXML
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java
index d9e1d8fc62dee9745734c99a475b77898a0bf5ea..a55c2346f4d9d209f04bfed7611d3d19dbc68382 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/TriggerPadTabViewController.java
@@ -48,10 +48,12 @@ public class TriggerPadTabViewController extends PadSettingsTabViewController im
 
 		// Sort the types for the tree view
 		for (TriggerPoint point : TriggerPoint.values()) {
-			Trigger trigger = triggers.get(point);
+			if (point.isAvailable(pad)) {
+				Trigger trigger = triggers.get(point);
 
-			TreeItem<TriggerDisplayable> triggerItem = new TreeItem<>(new TriggerDisplayable(trigger));
-			rootItem.getChildren().add(triggerItem);
+				TreeItem<TriggerDisplayable> triggerItem = new TreeItem<>(new TriggerDisplayable(trigger));
+				rootItem.getChildren().add(triggerItem);
+			}
 		}
 
 		treeView.setRoot(rootItem);
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java
index 1168ae13aedb828a12ba14ff18c14bad68718a1d..1d58f123a2ceb0ab27de8d3dda69a55e48501112 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/CartTriggerViewController.java
@@ -3,9 +3,9 @@ package de.tobias.playpad.viewcontroller.option.pad.trigger;
 import de.thecodelabs.utils.ui.NVC;
 import de.thecodelabs.utils.util.Localization;
 import de.tobias.playpad.PlayPadMain;
-import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.PadStatus;
 import de.tobias.playpad.project.Project;
+import de.tobias.playpad.project.api.IPad;
 import de.tobias.playpad.trigger.CartTriggerItem;
 import de.tobias.playpad.view.main.ProjectPreviewView;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
@@ -35,12 +35,12 @@ public class CartTriggerViewController extends NVC {
 		this.item = item;
 
 		Project project = PlayPadMain.getProgramInstance().getCurrentProject();
-		final List<Pad> pads = item.getCarts().stream().map(project::getPad).collect(Collectors.toList());
+		final List<? extends IPad> pads = item.getCarts().stream().map(project::getPad).collect(Collectors.toList());
 		projectPreviewView = new ProjectPreviewView(project, pads, mainViewController.getPage());
 		projectPreviewView.setPadding(new Insets(0, 0, 0, 164));
 		projectPreviewView.selectedProperty().addListener((InvalidationListener) observable -> {
 			item.getCarts().clear();
-			for (Pad pad : projectPreviewView.getSelected()) {
+			for (IPad pad : projectPreviewView.getSelected()) {
 				item.getCarts().add(pad.getUuid());
 			}
 		});
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java
index 85793e58329cc0793f8919085cf1059bd01cb500..aefadb33f2cfa1caf2319ba151ee3d0f9b2326af 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/TriggerPointViewController.java
@@ -69,8 +69,8 @@ public class TriggerPointViewController extends NVC {
 		try {
 			TriggerItemFactory connect = PlayPadPlugin.getRegistries().getTriggerItems().getFactory(item.getType());
 
-			VBox itemBox = new VBox(14);
-			NVC controller = connect.getSettingsController(item, mainViewController);
+			final VBox itemBox = new VBox(14);
+			final NVC controller = connect.getSettingsController(item, mainViewController);
 			if (controller != null) {
 				itemBox.getChildren().add(controller.getParent());
 
@@ -79,11 +79,11 @@ public class TriggerPointViewController extends NVC {
 					itemBox.getChildren().add(timeViewController.getParent());
 				}
 
-				Button deleteButton = new Button("", new FontIcon(FontAwesomeType.TRASH));
-				HBox hbox = new HBox(itemBox, deleteButton);
+				final Button deleteButton = new Button("", new FontIcon(FontAwesomeType.TRASH));
+				final HBox hbox = new HBox(itemBox, deleteButton);
 				hbox.setSpacing(14);
 
-				VBox rootBox = new VBox(14.0, hbox, new Separator());
+				final VBox rootBox = new VBox(14.0, hbox, new Separator());
 
 				itemView.getChildren().addAll(rootBox);
 
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java
index f48a57341e8a354af30dafc4ead00f256d7955f4..66718e15186b963c68d064339c0ac51aa15581c5 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/pad/trigger/VolumeTriggerViewController.java
@@ -6,6 +6,7 @@ import de.tobias.playpad.PlayPadMain;
 import de.tobias.playpad.Strings;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.project.Project;
+import de.tobias.playpad.project.api.IPad;
 import de.tobias.playpad.trigger.VolumeTriggerItem;
 import de.tobias.playpad.view.main.ProjectPreviewView;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
@@ -48,7 +49,7 @@ public class VolumeTriggerViewController extends NVC {
 		projectPreviewView.setPadding(new Insets(0, 0, 0, 164));
 		projectPreviewView.selectedProperty().addListener((InvalidationListener) observable -> {
 			item.getCarts().clear();
-			for (Pad pad : projectPreviewView.getSelected()) {
+			for (IPad pad : projectPreviewView.getSelected()) {
 				item.getCarts().add(pad.getUuid());
 			}
 		});
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/GeneralTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/GeneralTabViewController.java
index a1b33fa0d76fac292444eab77e09739446228f65..ee68975eac82cdee8cf838c1c616733af5af04e1 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/GeneralTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/GeneralTabViewController.java
@@ -9,6 +9,7 @@ import de.thecodelabs.utils.util.NumberUtils;
 import de.tobias.playpad.PlayPadPlugin;
 import de.tobias.playpad.Strings;
 import de.tobias.playpad.settings.GlobalSettings;
+import de.tobias.playpad.view.PseudoClasses;
 import de.tobias.playpad.viewcontroller.option.GlobalSettingsTabViewController;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
@@ -52,6 +53,13 @@ public class GeneralTabViewController extends GlobalSettingsTabViewController {
 	@FXML
 	private RadioButton settingsDisable;
 
+	@FXML
+	private CheckBox enableAutosaveCheckbox;
+	@FXML
+	private TextField autosaveIntervalTextField;
+
+	private static final String DIGIT_POSITIVE = "^[1-9]\\d*$";
+
 	private Alertable alertable;
 
 	public GeneralTabViewController(Alertable alertable) {
@@ -73,6 +81,7 @@ public class GeneralTabViewController extends GlobalSettingsTabViewController {
 		settingsGroup.getToggles().addAll(settingsEnable, settingsDisable);
 
 		liveModeCheckBox.selectedProperty().addListener((observable, oldValue, newValue) -> disableLiveSettings(newValue));
+		enableAutosaveCheckbox.selectedProperty().addListener((observable, oldValue, newValue) -> autosaveIntervalTextField.setDisable(!newValue));
 	}
 
 	private void disableLiveSettings(Boolean enableLiveSettings) {
@@ -168,6 +177,11 @@ public class GeneralTabViewController extends GlobalSettingsTabViewController {
 			settingsDisable.setSelected(true);
 
 		disableLiveSettings(settings.isLiveMode());
+
+		enableAutosaveCheckbox.setSelected(settings.isEnableAutosave());
+		autosaveIntervalTextField.setText(String.valueOf(settings.getAutosaveIntervalInMinutes()));
+		autosaveIntervalTextField.setDisable(!settings.isEnableAutosave());
+		autosaveIntervalTextField.textProperty().addListener((a, b, c) -> autosaveIntervalTextField.pseudoClassStateChanged(PseudoClasses.ERROR_CLASS, !c.matches(DIGIT_POSITIVE) || c.isEmpty()));
 	}
 
 	@Override
@@ -181,6 +195,9 @@ public class GeneralTabViewController extends GlobalSettingsTabViewController {
 		settings.setLiveModeDrag(dragEnable.isSelected());
 		settings.setLiveModeFile(fileEnable.isSelected());
 		settings.setLiveModeSettings(settingsEnable.isSelected());
+
+		settings.setEnableAutosave(enableAutosaveCheckbox.isSelected());
+		settings.setAutosaveIntervalInMinutes(Integer.parseInt(autosaveIntervalTextField.getText()));
 	}
 
 	@Override
@@ -190,7 +207,12 @@ public class GeneralTabViewController extends GlobalSettingsTabViewController {
 
 	@Override
 	public boolean validSettings() {
-		return true;
+		try {
+			final int autosaveInterval = Integer.parseInt(autosaveIntervalTextField.getText());
+			return autosaveInterval > 0;
+		} catch (NumberFormatException e) {
+			return false;
+		}
 	}
 
 	@Override
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/MappingTabViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/MappingTabViewController.java
index a184e370feeb0fb15feec5d6325aba4e58fba057..c783b375be86c4a9dda8d13a7e3a1a0a626fdaaa 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/MappingTabViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/MappingTabViewController.java
@@ -17,7 +17,7 @@ import de.tobias.playpad.Strings;
 import de.tobias.playpad.action.ActionProvider;
 import de.tobias.playpad.action.ActionType;
 import de.tobias.playpad.action.feedback.ColorAdjuster;
-import de.tobias.playpad.action.feedback.LightMode;
+import de.tobias.playpad.action.feedback.FeedbackColorSuggester;
 import de.tobias.playpad.action.settings.ActionSettingsEntry;
 import de.tobias.playpad.action.settings.ActionSettingsMappable;
 import de.tobias.playpad.profile.Profile;
@@ -27,7 +27,7 @@ import de.tobias.playpad.registry.Component;
 import de.tobias.playpad.viewcontroller.BaseMapperListViewController;
 import de.tobias.playpad.viewcontroller.IMappingTabViewController;
 import de.tobias.playpad.viewcontroller.cell.DisplayableTreeCell;
-import de.tobias.playpad.viewcontroller.cell.EnumCell;
+import de.tobias.playpad.viewcontroller.cell.LocalizeCell;
 import de.tobias.playpad.viewcontroller.cell.MappingListCell;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
 import de.tobias.playpad.viewcontroller.option.IProfileReloadTask;
@@ -77,7 +77,7 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 	private ComboBox<String> deviceComboBox;
 
 	@FXML
-	private ComboBox<LightMode> lightModeComboBox;
+	private ComboBox<String> midiColorMappingComboBox;
 
 	// Main View
 	@FXML
@@ -141,9 +141,20 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 		});
 		treeView.setCellFactory(list -> new DisplayableTreeCell<>());
 
-		lightModeComboBox.getItems().setAll(LightMode.values());
-		lightModeComboBox.setCellFactory(list -> new EnumCell<>("LightMode."));
-		lightModeComboBox.setButtonCell(new EnumCell<>("LightMode."));
+		initMidiColorMappingComboBox();
+		midiColorMappingComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
+			final ProfileSettings profileSettings = Profile.currentProfile().getProfileSettings();
+			profileSettings.setMidiColorMapping(midiColorMappingComboBox.getValue());
+		});
+	}
+
+	private void initMidiColorMappingComboBox() {
+		if (Midi.getInstance().getFeedbackTranscript() instanceof FeedbackColorSuggester) {
+			final FeedbackColorSuggester feedbackColorSuggester = (FeedbackColorSuggester) Midi.getInstance().getFeedbackTranscript();
+			midiColorMappingComboBox.getItems().setAll(feedbackColorSuggester.getMidiColorMappings());
+			midiColorMappingComboBox.setCellFactory(list -> new LocalizeCell("MidiColorMapping."));
+			midiColorMappingComboBox.setButtonCell(new LocalizeCell("MidiColorMapping."));
+		}
 	}
 
 	private TreeItem<ActionSettingsEntry> createTreeView(Mapping mapping) {
@@ -230,6 +241,7 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 				}
 			}
 		}
+		initMidiColorMappingComboBox();
 	}
 
 	@SuppressWarnings("Duplicates")
@@ -325,18 +337,21 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 		TextInputDialog dialog = new TextInputDialog();
 		dialog.initModality(Modality.WINDOW_MODAL);
 		dialog.initOwner(getContainingWindow());
-		dialog.setHeaderText("Umbenennen");
+		dialog.setHeaderText("Name");
 		dialog.setContentText("Geben Sie einen Namen für das Mapping Profil ein."); // TODO Localize
-		dialog.showAndWait().filter(s -> !s.isEmpty()).ifPresent(preset::setName);
+		dialog.showAndWait().filter(s -> !s.isEmpty()).ifPresent(name -> {
+			preset.setName(name);
 
-		final MappingCollection mappings = Profile.currentProfile().getMappings();
-		mappings.addMapping(preset);
-		mappingComboBox.getItems().add(preset);
-		mappingComboBox.getSelectionModel().select(preset);
+			final MappingCollection mappings = Profile.currentProfile().getMappings();
+			mappings.addMapping(preset);
+			mappingComboBox.getItems().add(preset);
+			mappingComboBox.getSelectionModel().select(preset);
+
+			if (mappings.count() > 1) {
+				mappingDeleteButton.setDisable(false);
+			}
+		});
 
-		if (mappings.count() > 1) {
-			mappingDeleteButton.setDisable(false);
-		}
 	}
 
 	@SuppressWarnings("Duplicates")
@@ -392,7 +407,7 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 		midiActiveCheckBox.setSelected(profileSettings.isMidiActive());
 		deviceComboBox.setDisable(!profileSettings.isMidiActive());
 		deviceComboBox.setValue(profileSettings.getMidiDevice());
-		lightModeComboBox.setValue(profileSettings.getLightMode());
+		midiColorMappingComboBox.setValue(profileSettings.getMidiColorMapping());
 	}
 
 	@Override
@@ -401,7 +416,6 @@ public class MappingTabViewController extends ProfileSettingsTabViewController i
 
 		// Midi
 		profileSettings.setMidiActive(isMidiActive());
-		profileSettings.setLightMode(lightModeComboBox.getValue());
 	}
 
 	@Override
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/ProfileSettingsViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/ProfileSettingsViewController.java
index 4f0ab95348e67855818b1aaf81a63b5a9010a4e7..1b5fe1066e2f2a83e0d1553ebe24cced6b5dd1af 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/ProfileSettingsViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/profile/ProfileSettingsViewController.java
@@ -143,9 +143,7 @@ public class ProfileSettingsViewController extends NVC implements IProfileSettin
 	// Button Listener
 	@FXML
 	private void finishButtonHandler(ActionEvent event) {
-		if (onFinish()) {
-			getStageContainer().ifPresent(NVCStage::close);
-		}
+		getStageContainer().ifPresent(NVCStage::close);
 	}
 
 	/**
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/project/ProjectSettingsViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/project/ProjectSettingsViewController.java
index 9cab45389cb4dbdc4fa911f4d16fe0c74d5b04c9..d7207797c3ac53c738aa6901dd20d8c5625dd775 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/project/ProjectSettingsViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/option/project/ProjectSettingsViewController.java
@@ -96,9 +96,7 @@ public class ProjectSettingsViewController extends NVC implements IProjectSettin
 	// Button Listener
 	@FXML
 	private void finishButtonHandler(ActionEvent event) {
-		if (onFinish()) {
-			getStageContainer().ifPresent(NVCStage::close);
-		}
+		getStageContainer().ifPresent(NVCStage::close);
 	}
 
 	/**
diff --git a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/settings/WarningFeedbackViewController.java b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/settings/WarningFeedbackViewController.java
index dfe3087c7e911b035ddfa39ba83f795ed0eaa19a..74762ff9671799447a7436b74c3a2a54df3763db 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/settings/WarningFeedbackViewController.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/viewcontroller/settings/WarningFeedbackViewController.java
@@ -34,7 +34,7 @@ public class WarningFeedbackViewController extends NVC {
 		return controller;
 	}
 
-	public static  WarningFeedbackViewController newViewControllerForPad() {
+	public static WarningFeedbackViewController newViewControllerForPad() {
 		return new WarningFeedbackViewController();
 	}
 
diff --git a/PlayWall/src/main/resources/components/Actions.xml b/PlayWall/src/main/resources/components/Actions.xml
index f992162e06c0982a968ecd169612c8e66f657c9a..39e66640e5914e47b8530f1d521661f7cfdd377a 100644
--- a/PlayWall/src/main/resources/components/Actions.xml
+++ b/PlayWall/src/main/resources/components/Actions.xml
@@ -1,16 +1,24 @@
 <Actions>
-    <Component id="CartAction" name="Action.Cart.Name" icon="TH" class="de.thecodelabs.utils.ui.icon.FontAwesomeType"
+    <Component id="CartAction" name="Action.Cart.Name" icon="TH"
+               class="de.thecodelabs.utils.ui.icon.FontAwesomeType"
                size="11">
         de.tobias.playpad.action.factory.CartActionProvider
     </Component>
-    <Component id="PageAction" name="Action.Page.Name" icon="FILE_TEXT" class="de.thecodelabs.utils.ui.icon.FontAwesomeType"
+    <Component id="PlaylistNextAction" name="Action.PlaylistNext.Name" icon="TH"
+               class="de.thecodelabs.utils.ui.icon.FontAwesomeType"
+               size="11">
+        de.tobias.playpad.action.factory.PlaylistNextActionProvider
+    </Component>
+    <Component id="PageAction" name="Action.Page.Name" icon="FILE_TEXT"
+               class="de.thecodelabs.utils.ui.icon.FontAwesomeType"
                size="11">de.tobias.playpad.action.factory.PageActionProvider
     </Component>
     <Component id="NavigateAction" name="Action.Navigate.Name" icon="NAVIGATION"
                class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon" size="11">
         de.tobias.playpad.action.factory.NavigateActionProvider
     </Component>
-    <Component id="StopAction" name="Action.Stop.Name" icon="STOP" class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon"
+    <Component id="StopAction" name="Action.Stop.Name" icon="STOP"
+               class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon"
                size="11">de.tobias.playpad.action.factory.StopActionProvider
     </Component>
 </Actions>
\ No newline at end of file
diff --git a/PlayWall/src/main/resources/lang/_de.properties b/PlayWall/src/main/resources/lang/_de.properties
index 33029a528b99644493e53bcd9b83e304ae7fd03d..e583b7e22368d531c167a9310fd19018bc4e63e8 100755
--- a/PlayWall/src/main/resources/lang/_de.properties
+++ b/PlayWall/src/main/resources/lang/_de.properties
@@ -167,6 +167,7 @@ Action.Cart.toString=Kachel {}
 Action.Page.toString=Seite {}
 Action.Navigate.toString={}
 Action.Cart.Name=Kacheln
+Action.PlaylistNext.Name=Playlist
 Action.Stop.Name=Stop
 Action.Page.Name=Seiten
 Action.Navigate.Name=Navigation
@@ -214,7 +215,10 @@ Trigger.Volume.Name=Lautst\u00E4rke
 TriggerPoint.START=Start
 TriggerPoint.STOP=Stop
 TriggerPoint.EOF=Ende
-TriggerPoint.EOF_STATE=Ende (EoF)
+TriggerPoint.PLAYLIST_START=Playlist Start
+TriggerPoint.PLAYLIST_ITEM_START=Playlist Item Start
+TriggerPoint.PLAYLIST_ITEM_END=Playlist Item Ende
+TriggerPoint.PLAYLIST_END=Playlist Ende
 
 # Drag and Drop Mode
 DnDMode.Replace=Ersetzen
@@ -264,9 +268,3 @@ Server.Error.IO=Anmeldung fehlgeschlagen. Der Server ist nicht erreichbar. Versu
 Server.Error.Login=Anmeldung fehlgeschlagen. Der Nutzername oder das Passwort sind nicht korrekt.
 
 Auth.Logout=Alle Onlineprojekte sind nicht mehr lokal verf\u00FCgbar.
-
-#LightMode Enum
-LightMode.LOW=Niedrig
-LightMode.MIDDLE=Mittel
-LightMode.NORMAL=Hell
-LightMode.HIGH=Billiant
\ No newline at end of file
diff --git a/PlayWall/src/main/resources/lang/ui_de.properties b/PlayWall/src/main/resources/lang/ui_de.properties
index f3e03f795141542e4f17f58a3af51a51dfca4be3..062aace8ef256641cf4fb3fef49f8197dfb38747 100755
--- a/PlayWall/src/main/resources/lang/ui_de.properties
+++ b/PlayWall/src/main/resources/lang/ui_de.properties
@@ -51,6 +51,10 @@ settings.gen.cache.label=Cachespeicher:
 settings.gen.cache.button.choose=W\u00E4hlen
 settings.gen.cache.button.reset=Cache leeren
 settings.gen.cache.label.info=Wenn Sie als Audioausgabetyp "Java Audiostream" gew\u00E4hlt haben, werden s\u00E4mtliche MP3-Dateien in eine WAV-Dateien umgewandelt. Diese wird dann in den oben angegebenen Ordner abgelegt. Sollten Sie viele Dateien aus Ihrem Projekt gel\u00F6scht haben, k\u00F6nnen Sie den Cache leeren, um den ben\u00F6tigten Speicherplatz zu verringern. Ben\u00F6tigte Dateien werden automatisch neu umgewandelt, falls sie nicht (mehr) existieren.
+settings.gen.label.autosave=Automatisches Speichern
+settings.gen.checkbox.enableAutosave=Automatisches Speichern aktivieren
+settings.key.gen.autosave.all=Alle
+settings.key.gen.autosave.minutes=Minuten
 settings.mapping.label.mapping=Mapping:
 settings.mapping.button.edit=Bearbeiten...
 settings.mapping.button.edit.new=Neu...
@@ -93,8 +97,8 @@ settings.paths.button.choose=W\u00E4hlen...
 settings.paths.checkbox.mediaActive=Mediendateien beim Import in den Medienordner kopieren
 settings.button.finish=Fertig
 settings.checkbox.activate=Aktivieren
-settings.label.lightmode=Helligkeit
-settings.label.lightmode.info=(Nur f\u00FCr LaunchPad MK2 unterst\u00FCtzt)
+settings.label.midiColorMapping=Farbmodus
+settings.label.midiColorMapping.info=(Nur f\u00FCr LaunchPad MK2 unterst\u00FCtzt)
 
 plugins.button.install=Installieren
 plugins.button.uninstall=Deinstallieren
@@ -128,11 +132,16 @@ padSettings.gen.label.timeDisplay=Zeitanzeige:
 padSettings.gen.checkbox.customSettings=Eigene Einstellungen
 padSettings.label.loop=Wiederholen:
 padSettings.button.finish=Fertig
+padSettings.player.label.playOverlay=Play Overlay:
+padSettings.player.label.playOverlay.description=Mit dieser Einstellung darf diese Kachel zus\u00E4tzlich zu einer bereits laufenden wiedergegeben werden, wenn der Modus "Mehrere Kacheln wiedergaben" deakitiviert ist.
 padSettings.player.label.warning=Warnhinweise:
 padSettings.player.label.cueIn=Intro Dauer (in s):
 padSettings.player.label.fade=Ein-/Ausblenden:
 padSettings.layout.label.custom=Eigenes Layout:
 padSettings.layout.checkbox.custom=Aktiviert
+padSettings.playlist.settings.title=Allgemeine Einstellungen
+padSettings.playlist.settings.shuffle=Zuf\u00E4llige Wiedergabe
+padSettings.playlist.settings.autoNext=N\u00E4chsten Eintrag automatisch starten (Standard)
 project.label.details=Projektinformationen:
 project.label.name=Name:
 project.label.profile=Profil:
diff --git a/PlayWall/src/main/resources/style/modern-pad-cue-in.css b/PlayWall/src/main/resources/style/modern-pad-cue-in.css
new file mode 100644
index 0000000000000000000000000000000000000000..65ea71f1be01bb6a98b7e566b10c8e995bd4c33d
--- /dev/null
+++ b/PlayWall/src/main/resources/style/modern-pad-cue-in.css
@@ -0,0 +1,4 @@
+
+.pad${#prefix}-cue-in {
+    -fx-background-color: ${#padCueInColor} !important;
+}
\ No newline at end of file
diff --git a/PlayWall/src/main/resources/style/modern-pad.css b/PlayWall/src/main/resources/style/modern-pad.css
index abd17dbdf936ab7d80a0c9fd7a022c98935d9669..11df504e7820334526c06d2003bd0ce425313b1c 100644
--- a/PlayWall/src/main/resources/style/modern-pad.css
+++ b/PlayWall/src/main/resources/style/modern-pad.css
@@ -21,15 +21,10 @@
 	-fx-background-color: ${#padColor};
 }
 
-.pad${#prefix}-cue-in${#class} {
-	-fx-background-color: ${#padCueInColor};
-}
-
-
 .pad${#prefix}-info${#class} {
 	-fx-text-fill: ${#fontColor};
 }
 
 .pad${#prefix}-title${#class} {
 	-fx-text-fill: ${#fontColor};
-}
\ No newline at end of file
+}
diff --git a/PlayWall/src/main/resources/view/option/global/GeneralTab.fxml b/PlayWall/src/main/resources/view/option/global/GeneralTab.fxml
index 4e22ed2761ce9224465d7bb48721f063528b547f..b785d0da35bd9b55dd80461742dc6e9b7f50bb06 100644
--- a/PlayWall/src/main/resources/view/option/global/GeneralTab.fxml
+++ b/PlayWall/src/main/resources/view/option/global/GeneralTab.fxml
@@ -7,108 +7,105 @@
     <children>
         <HBox spacing="14.0">
             <children>
-                <Label alignment="BASELINE_RIGHT" maxWidth="1.7976931348623157E308" prefWidth="150.0"
-                       text="%settings.gen.label.behaviour"/>
-                <CheckBox fx:id="openLastDocumentCheckbox" layoutX="30.0" layoutY="269.0" mnemonicParsing="false"
-                          text="%settings.gen.checkbox.openLastDocument" AnchorPane.leftAnchor="30.0"
-                          AnchorPane.topAnchor="269.0"/>
+                <Label alignment="BASELINE_RIGHT" maxWidth="1.7976931348623157E308" prefWidth="150.0" text="%settings.gen.label.behaviour" />
+                <CheckBox fx:id="openLastDocumentCheckbox" layoutX="30.0" layoutY="269.0" mnemonicParsing="false" text="%settings.gen.checkbox.openLastDocument" AnchorPane.leftAnchor="30.0" AnchorPane.topAnchor="269.0" />
             </children>
         </HBox>
-        <Separator prefWidth="200.0"/>
+        <Separator prefWidth="200.0" />
         <HBox spacing="14.0">
             <children>
-                <Label alignment="BASELINE_RIGHT" maxWidth="1.7976931348623157E308" prefWidth="150.0"
-                       text="%settings.gen.label.liveMode"/>
-                <CheckBox fx:id="liveModeCheckBox" layoutX="30.0" layoutY="269.0" mnemonicParsing="false"
-                          text="%settings.gen.checkbox.liveMode" AnchorPane.leftAnchor="30.0"
-                          AnchorPane.topAnchor="269.0"/>
+                <Label alignment="BASELINE_RIGHT" maxWidth="1.7976931348623157E308" prefWidth="150.0" text="%settings.gen.label.liveMode" />
+                <CheckBox fx:id="liveModeCheckBox" layoutX="30.0" layoutY="269.0" mnemonicParsing="false" text="%settings.gen.checkbox.liveMode" AnchorPane.leftAnchor="30.0" AnchorPane.topAnchor="269.0" />
             </children>
         </HBox>
         <HBox spacing="14.0">
             <children>
-                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.pageChange"/>
-                <RadioButton fx:id="pageEnable" mnemonicParsing="false" prefWidth="125.0"
-                             text="%settings.gen.radio.liveMode.enable"/>
-                <RadioButton fx:id="pageDisable" mnemonicParsing="false" prefWidth="100.0"
-                             text="%settings.gen.radio.liveMode.disable"/>
+                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.pageChange" />
+                <RadioButton fx:id="pageEnable" mnemonicParsing="false" prefWidth="125.0" text="%settings.gen.radio.liveMode.enable" />
+                <RadioButton fx:id="pageDisable" mnemonicParsing="false" prefWidth="100.0" text="%settings.gen.radio.liveMode.disable" />
             </children>
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
         </HBox>
         <HBox spacing="14.0">
             <children>
-                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.dragPads"/>
-                <RadioButton fx:id="dragEnable" mnemonicParsing="false" prefWidth="125.0"
-                             text="%settings.gen.radio.liveMode.enable"/>
-                <RadioButton fx:id="dragDisable" mnemonicParsing="false" prefWidth="100.0"
-                             text="%settings.gen.radio.liveMode.disable"/>
+                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.dragPads" />
+                <RadioButton fx:id="dragEnable" mnemonicParsing="false" prefWidth="125.0" text="%settings.gen.radio.liveMode.enable" />
+                <RadioButton fx:id="dragDisable" mnemonicParsing="false" prefWidth="100.0" text="%settings.gen.radio.liveMode.disable" />
             </children>
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
         </HBox>
         <HBox spacing="14.0">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
             <children>
-                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.media"/>
-                <RadioButton fx:id="fileEnable" mnemonicParsing="false" prefWidth="125.0"
-                             text="%settings.gen.radio.liveMode.enable"/>
-                <RadioButton fx:id="fileDisable" mnemonicParsing="false" prefWidth="100.0"
-                             text="%settings.gen.radio.liveMode.disable"/>
+                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.media" />
+                <RadioButton fx:id="fileEnable" mnemonicParsing="false" prefWidth="125.0" text="%settings.gen.radio.liveMode.enable" />
+                <RadioButton fx:id="fileDisable" mnemonicParsing="false" prefWidth="100.0" text="%settings.gen.radio.liveMode.disable" />
             </children>
         </HBox>
         <HBox spacing="14.0">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
             <children>
-                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.settings"/>
-                <RadioButton fx:id="settingsEnable" mnemonicParsing="false" prefWidth="125.0"
-                             text="%settings.gen.radio.liveMode.enable"/>
-                <RadioButton fx:id="settingsDisable" mnemonicParsing="false" prefWidth="100.0"
-                             text="%settings.gen.radio.liveMode.disable"/>
+                <Label prefWidth="150.0" text="%settings.gen.label.liveMode.settings" />
+                <RadioButton fx:id="settingsEnable" mnemonicParsing="false" prefWidth="125.0" text="%settings.gen.radio.liveMode.enable" />
+                <RadioButton fx:id="settingsDisable" mnemonicParsing="false" prefWidth="100.0" text="%settings.gen.radio.liveMode.disable" />
             </children>
         </HBox>
         <Label text="%settings.gen.label.liveModeInfo" wrapText="true">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
         </Label>
-        <Separator prefWidth="200.0"/>
+        <Separator prefWidth="200.0" />
         <HBox spacing="14.0">
             <children>
-                <Label alignment="BASELINE_RIGHT" maxHeight="1.7976931348623157E308" prefWidth="150.0"
-                       text="%settings.gen.cache.label"/>
-                <TextField fx:id="cacheTextField" promptText="C:\Users\Max\Cache" HBox.hgrow="ALWAYS"/>
-                <Button mnemonicParsing="false" onAction="#cacheChooseHandler"
-                        text="%settings.gen.cache.button.choose"/>
+                <Label alignment="BASELINE_RIGHT" maxHeight="1.7976931348623157E308" prefWidth="150.0" text="%settings.gen.cache.label" />
+                <TextField fx:id="cacheTextField" promptText="C:\Users\Max\Cache" HBox.hgrow="ALWAYS" />
+                <Button mnemonicParsing="false" onAction="#cacheChooseHandler" text="%settings.gen.cache.button.choose" />
             </children>
         </HBox>
         <HBox alignment="CENTER_LEFT" spacing="14.0">
             <children>
-                <Label prefWidth="150.0"/>
-                <Button mnemonicParsing="false" onAction="#cacheResetButtonHandler"
-                        text="%settings.gen.cache.button.reset"/>
-                <Label fx:id="cacheSizeLabel" text="Label"/>
+                <Label prefWidth="150.0" />
+                <Button mnemonicParsing="false" onAction="#cacheResetButtonHandler" text="%settings.gen.cache.button.reset" />
+                <Label fx:id="cacheSizeLabel" text="Label" />
             </children>
         </HBox>
         <Label text="%settings.gen.cache.label.info" wrapText="true">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
         </Label>
-        <Separator prefWidth="200.0"/>
-        <Button alignment="TOP_LEFT" mnemonicParsing="false" onAction="#resetDialogs"
-                text="%settings.gen.warning.button.reset">
+        <Separator prefWidth="200.0" />
+        <Button alignment="TOP_LEFT" mnemonicParsing="false" onAction="#resetDialogs" text="%settings.gen.warning.button.reset">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </VBox.margin>
         </Button>
+      <Separator prefWidth="200.0" />
+      <HBox spacing="14.0">
+         <children>
+            <Label alignment="BASELINE_RIGHT" maxWidth="1.7976931348623157E308" prefWidth="150.0" text="%settings.gen.label.autosave" />
+            <CheckBox fx:id="enableAutosaveCheckbox" layoutX="30.0" layoutY="269.0" mnemonicParsing="false" text="%settings.gen.checkbox.enableAutosave" AnchorPane.leftAnchor="30.0" AnchorPane.topAnchor="269.0" />
+         </children>
+      </HBox>
+      <HBox alignment="CENTER_LEFT" spacing="14.0">
+         <children>
+            <Label prefWidth="150.0" />
+            <Label text="%settings.key.gen.autosave.all" />
+            <TextField fx:id="autosaveIntervalTextField" prefHeight="25.0" prefWidth="51.0" />
+            <Label text="%settings.key.gen.autosave.minutes" />
+         </children>
+      </HBox>
     </children>
     <padding>
-        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/>
+        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
     </padding>
 </VBox>
diff --git a/PlayWall/src/main/resources/view/option/layout/ModernLayoutCart.fxml b/PlayWall/src/main/resources/view/option/layout/ModernLayoutCart.fxml
index 4d3d165be6852b06cfdf2750f2bb8af7b6be3023..f970df1f7b86d32e56856cb15ae68af01b792007 100644
--- a/PlayWall/src/main/resources/view/option/layout/ModernLayoutCart.fxml
+++ b/PlayWall/src/main/resources/view/option/layout/ModernLayoutCart.fxml
@@ -9,43 +9,39 @@
             <children>
                 <VBox alignment="BOTTOM_LEFT" prefWidth="150.0">
                     <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%layout.label.color"
-                               VBox.vgrow="ALWAYS">
+                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%layout.label.color" VBox.vgrow="ALWAYS">
                             <VBox.margin>
-                                <Insets bottom="5.0"/>
+                                <Insets bottom="5.0" />
                             </VBox.margin>
                         </Label>
                     </children>
                 </VBox>
                 <VBox layoutX="110.0" layoutY="8.0" maxWidth="1.7976931348623157E308" prefWidth="125.0" spacing="14.0">
                     <children>
-                        <Label alignment="BOTTOM_LEFT" layoutX="110.0" layoutY="8.0" prefHeight="60.0" prefWidth="150.0"
-                               text="%layout.label.background" wrapText="true" VBox.vgrow="ALWAYS"/>
-                        <Button fx:id="backgroundColorButton" focusTraversable="false" mnemonicParsing="false"
-                                onAction="#backgroundColorButtonHandler" prefWidth="150.0"/>
+                  <CheckBox fx:id="backgroundColorCheckbox" mnemonicParsing="false" text="%padSettings.layout.checkbox.custom" />
+                        <Label alignment="BOTTOM_LEFT" layoutX="110.0" layoutY="8.0" prefWidth="150.0" text="%layout.label.background" wrapText="true" VBox.vgrow="ALWAYS" />
+                        <Button fx:id="backgroundColorButton" focusTraversable="false" mnemonicParsing="false" onAction="#backgroundColorButtonHandler" prefWidth="150.0" />
                     </children>
                 </VBox>
                 <VBox layoutX="243.0" layoutY="8.0" maxWidth="1.7976931348623157E308" prefWidth="125.0" spacing="14.0">
                     <children>
-                        <Label alignment="BOTTOM_LEFT" layoutX="243.0" layoutY="8.0" prefHeight="60.0" prefWidth="150.0"
-                               text="%layout.label.play" wrapText="true" VBox.vgrow="ALWAYS"/>
-                        <Button fx:id="playColorButton" focusTraversable="false" mnemonicParsing="false"
-                                onAction="#playColorButtonHandler" prefWidth="150.0"/>
+                  <CheckBox fx:id="playColorCheckbox" mnemonicParsing="false" text="%padSettings.layout.checkbox.custom" />
+                        <Label alignment="BOTTOM_LEFT" layoutX="243.0" layoutY="8.0" prefWidth="150.0" text="%layout.label.play" wrapText="true" VBox.vgrow="ALWAYS" />
+                        <Button fx:id="playColorButton" focusTraversable="false" mnemonicParsing="false" onAction="#playColorButtonHandler" prefWidth="150.0" />
                     </children>
                 </VBox>
                 <VBox layoutX="243.0" layoutY="8.0" maxWidth="1.7976931348623157E308" prefWidth="125.0" spacing="14.0">
                     <children>
-                        <Label alignment="BOTTOM_LEFT" layoutX="243.0" layoutY="8.0" prefHeight="60.0" prefWidth="150.0"
-                               text="%layout.label.cueIn" wrapText="true" VBox.vgrow="ALWAYS"/>
-                        <Button fx:id="cueInColorButton" focusTraversable="false" mnemonicParsing="false"
-                                onAction="#cueInColorButtonHandler" prefWidth="150.0"/>
+                  <CheckBox fx:id="cueInColorCheckbox" mnemonicParsing="false" text="%padSettings.layout.checkbox.custom" />
+                        <Label alignment="BOTTOM_LEFT" layoutX="243.0" layoutY="8.0" prefWidth="150.0" text="%layout.label.cueIn" wrapText="true" VBox.vgrow="ALWAYS" />
+                        <Button fx:id="cueInColorButton" focusTraversable="false" mnemonicParsing="false" onAction="#cueInColorButtonHandler" prefWidth="150.0" />
                     </children>
                 </VBox>
             </children>
         </HBox>
-        <Button fx:id="resetButton" mnemonicParsing="false" onAction="#resetButtonHandler" text="%layout.button.reset">
+        <Button fx:id="resetButton" alignment="CENTER" mnemonicParsing="false" onAction="#resetButtonHandler" text="%layout.button.reset">
             <VBox.margin>
-                <Insets left="164.0"/>
+                <Insets left="164.0" top="28.0" />
             </VBox.margin>
         </Button>
     </children>
diff --git a/PlayWall/src/main/resources/view/option/pad/LayoutTab.fxml b/PlayWall/src/main/resources/view/option/pad/LayoutTab.fxml
index fa3222571bdfb80064b4638bb51585ef124bc1aa..ff2aa1d447ffe99dadc1db1358a8bc5b02e35c4a 100644
--- a/PlayWall/src/main/resources/view/option/pad/LayoutTab.fxml
+++ b/PlayWall/src/main/resources/view/option/pad/LayoutTab.fxml
@@ -1,27 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-
 <?import javafx.geometry.Insets?>
-<?import javafx.scene.control.CheckBox?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.layout.*?>
 <VBox spacing="14.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
     <children>
-        <HBox layoutX="14.0" layoutY="13.0" spacing="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0"
-              AnchorPane.topAnchor="14.0">
+        <HBox layoutX="14.0" layoutY="13.0" spacing="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="14.0">
             <children>
-                <Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="14.0" prefWidth="150.0"
-                       text="%padSettings.layout.label.custom"/>
-                <CheckBox fx:id="enableLayoutCheckBox" layoutX="127.0" layoutY="13.0" mnemonicParsing="false"
-                          text="%padSettings.layout.checkbox.custom"/>
+                <Label alignment="CENTER_RIGHT" layoutX="14.0" layoutY="14.0" prefWidth="150.0" text="%padSettings.layout.label.custom" />
             </children>
             <VBox.margin>
-                <Insets/>
+                <Insets />
             </VBox.margin>
         </HBox>
-        <VBox fx:id="layoutContainer" prefHeight="200.0" prefWidth="100.0"/>
+        <VBox fx:id="layoutContainer" prefHeight="200.0" prefWidth="100.0" />
     </children>
     <padding>
-        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/>
+        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
     </padding>
 </VBox>
diff --git a/PlayWall/src/main/resources/view/option/pad/PlayerTab.fxml b/PlayWall/src/main/resources/view/option/pad/PlayerTab.fxml
index 9f5771e57c8f2ffd7234c4c002ec2b7de8a800d5..ec40e05612d22b9b2ef1ee6456a5bc47042a2ff3 100644
--- a/PlayWall/src/main/resources/view/option/pad/PlayerTab.fxml
+++ b/PlayWall/src/main/resources/view/option/pad/PlayerTab.fxml
@@ -5,6 +5,22 @@
 <?import javafx.scene.layout.*?>
 <VBox spacing="14.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
     <children>
+        <VBox spacing="14.0">
+            <children>
+                <HBox layoutX="14.0" layoutY="139.0" spacing="14.0">
+                    <children>
+                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%padSettings.player.label.playOverlay" textAlignment="RIGHT" />
+                        <CheckBox fx:id="playOverlayEnableCheckBox" mnemonicParsing="false" text="%settings.checkbox.activate" />
+                    </children>
+                </HBox>
+            <Label text="%padSettings.player.label.playOverlay.description" wrapText="true">
+               <VBox.margin>
+                  <Insets left="164.0" />
+               </VBox.margin>
+            </Label>
+            </children>
+        </VBox>
+        <Separator prefWidth="200.0" />
         <VBox spacing="14.0">
             <children>
                 <HBox layoutX="14.0" layoutY="253.0" spacing="14.0">
diff --git a/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml b/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml
index 6f25be3e5792c953ce621d4ae05e0ca0db5b8c8f..e2d0b4d1b0cfdf211574d154ca3f48ef9a4cc31f 100644
--- a/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml
+++ b/PlayWall/src/main/resources/view/option/pad/PlaylistTab.fxml
@@ -1,51 +1,63 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<?import javafx.geometry.Insets?>
+<?import javafx.geometry.*?>
 <?import javafx.scene.control.*?>
 <?import javafx.scene.layout.*?>
-<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0"
-      xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
-    <children>
-        <VBox spacing="14.0">
-            <children>
-                <ListView fx:id="mediaPathListView" prefWidth="250.0" VBox.vgrow="ALWAYS"/>
-                <HBox spacing="14.0">
-                    <children>
-                        <Button fx:id="addButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false"
-                                onAction="#onAddHandler" HBox.hgrow="ALWAYS"/>
-                        <Button fx:id="upButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false"
-                                onAction="#onUpHandler" HBox.hgrow="ALWAYS"/>
-                        <Button fx:id="downButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false"
-                                onAction="#onDownAction" HBox.hgrow="ALWAYS"/>
-                    </children>
-                </HBox>
-            </children>
-        </VBox>
-        <VBox spacing="14.0">
-            <children>
-                <HBox spacing="14.0">
-                    <children>
-                        <Label fx:id="pathLabel" textOverrun="CENTER_ELLIPSIS"/>
-                    </children>
-                </HBox>
-                <HBox spacing="14.0">
-                    <children>
-                        <Button fx:id="showFileButton" mnemonicParsing="false" onAction="#onShowFileHandler"
-                                text="%padSettings.button.path.show"/>
-                        <Button fx:id="deleteButton" mnemonicParsing="false" onAction="#onDeleteHandler"
-                                text="%padSettings.button.delete"/>
-                    </children>
-                </HBox>
-            </children>
-            <HBox.margin>
-                <Insets/>
-            </HBox.margin>
-            <padding>
-                <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/>
-            </padding>
-        </VBox>
-    </children>
-    <padding>
-        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/>
-    </padding>
-</HBox>
+<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
+   <children>
+      <VBox spacing="14">
+         <children>
+            <Label styleClass="headline" text="%padSettings.playlist.settings.title" />
+            <CheckBox fx:id="shuffleCheckbox" mnemonicParsing="false" text="%padSettings.playlist.settings.shuffle" />
+            <CheckBox fx:id="autoNextCheckbox" mnemonicParsing="false" text="%padSettings.playlist.settings.autoNext" />
+         </children>
+         <VBox.margin>
+            <Insets />
+         </VBox.margin>
+         <padding>
+            <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+         </padding>
+      </VBox>
+      <HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0">
+          <children>
+              <VBox spacing="14.0">
+                  <children>
+                      <ListView fx:id="mediaPathListView" prefWidth="250.0" VBox.vgrow="ALWAYS" />
+                      <HBox spacing="14.0">
+                          <children>
+                              <Button fx:id="addButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onAddHandler" HBox.hgrow="ALWAYS" />
+                              <Button fx:id="upButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onUpHandler" HBox.hgrow="ALWAYS" />
+                              <Button fx:id="downButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onDownAction" HBox.hgrow="ALWAYS" />
+                          </children>
+                      </HBox>
+                  </children>
+              </VBox>
+              <VBox spacing="14.0">
+                  <children>
+                      <HBox spacing="14.0">
+                          <children>
+                              <Label fx:id="pathLabel" textOverrun="CENTER_ELLIPSIS" />
+                          </children>
+                      </HBox>
+                      <HBox spacing="14.0">
+                          <children>
+                              <Button fx:id="showFileButton" mnemonicParsing="false" onAction="#onShowFileHandler" text="%padSettings.button.path.show" />
+                              <Button fx:id="deleteButton" mnemonicParsing="false" onAction="#onDeleteHandler" text="%padSettings.button.delete" />
+                          </children>
+                      </HBox>
+                  <VBox fx:id="customItemView" />
+                  </children>
+                  <HBox.margin>
+                      <Insets />
+                  </HBox.margin>
+                  <padding>
+                      <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+                  </padding>
+              </VBox>
+          </children>
+          <padding>
+              <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+          </padding>
+      </HBox>
+   </children>
+</VBox>
diff --git a/PlayWall/src/main/resources/view/option/profile/Mapping.fxml b/PlayWall/src/main/resources/view/option/profile/Mapping.fxml
index d52b331da82a236ee33b08ed1d95c7ddf8efd3a0..77f8684ef88127b43b67361bdf1cc446c2c46e09 100644
--- a/PlayWall/src/main/resources/view/option/profile/Mapping.fxml
+++ b/PlayWall/src/main/resources/view/option/profile/Mapping.fxml
@@ -45,15 +45,15 @@
         </HBox>
       <HBox spacing="14.0">
          <children>
-            <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%settings.label.lightmode">
+            <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%settings.label.midiColorMapping">
                <HBox.margin>
                   <Insets top="4.0" />
                </HBox.margin>
             </Label>
             <HBox maxWidth="1.7976931348623157E308" spacing="14.0" HBox.hgrow="ALWAYS">
                <children>
-                  <ComboBox fx:id="lightModeComboBox" onAction="#deviceHandler" prefWidth="150.0" />
-                  <Label text="%settings.label.lightmode.info" />
+                  <ComboBox fx:id="midiColorMappingComboBox" onAction="#deviceHandler" prefWidth="150.0" />
+                  <Label text="%settings.label.midiColorMapping.info" />
                </children>
             </HBox>
          </children>
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.scala
deleted file mode 100644
index 68e7102c0ed6b2764605fc88aff46d9127d2972c..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandler.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.thecodelabs.midi.event.KeyEventType
-import de.tobias.playpad.action.actions.CartAction
-import de.tobias.playpad.pad.Pad
-
-trait CartActionHandler {
-
-	def performAction(keyEventType: KeyEventType, cartAction: CartAction, pad: Pad): Unit
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.scala
deleted file mode 100644
index 05a7a32b0777fcd0df6b628ecd9a407ec9860efc..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/CartActionHandlerFactory.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.tobias.playpad.action.actions.CartAction.CartActionMode
-
-object CartActionHandlerFactory {
-
-	def getInstance(cartActionMode: CartActionMode): CartActionHandler = {
-		cartActionMode match {
-			case CartActionMode.PLAY_STOP => new PlayStopHandler()
-			case CartActionMode.PLAY_PAUSE => new PlayPauseHandler()
-			case CartActionMode.PLAY_HOLD => new PlayHoldHandler()
-			case CartActionMode.PLAY_PLAY => new PlayPlayHandler()
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.scala
deleted file mode 100644
index 1aa63b52c8dbc071bcd89fb271272dadb25a0511..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayHoldHandler.scala
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.thecodelabs.midi.event.KeyEventType
-import de.tobias.playpad.action.actions.CartAction
-import de.tobias.playpad.pad.Pad
-
-class PlayHoldHandler extends CartActionHandler {
-
-	override def performAction(keyEventType: KeyEventType, cartAction: CartAction, pad: Pad): Unit = {
-		if (keyEventType eq KeyEventType.DOWN) {
-			if (pad.isReady) {
-				pad.play()
-			}
-		} else if (keyEventType eq KeyEventType.UP) {
-			if (pad.isPlay) {
-				pad.stop()
-			}
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.scala
deleted file mode 100644
index 87c26d4ed6f1aaf0c3afbbb8cc50cd8033381db0..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPauseHandler.scala
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.thecodelabs.midi.event.KeyEventType
-import de.tobias.playpad.action.actions.CartAction
-import de.tobias.playpad.pad.Pad
-
-class PlayPauseHandler extends CartActionHandler {
-
-	override def performAction(keyEventType: KeyEventType, cartAction: CartAction, pad: Pad): Unit = {
-		if (keyEventType eq KeyEventType.DOWN) {
-			if (pad.isPlay) {
-				pad.pause()
-			}
-			else { // Allow the listener to send the feedback
-				// cartAction.getPadPositionListener.setSend(false) TODO Fix
-				pad.play()
-			}
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.scala
deleted file mode 100644
index fec8eebc41c563e11a12bede31da19e7ae882141..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayPlayHandler.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.thecodelabs.midi.event.KeyEventType
-import de.tobias.playpad.action.actions.CartAction
-import de.tobias.playpad.pad.{Pad, PadStatus}
-
-class PlayPlayHandler extends CartActionHandler {
-
-	override def performAction(keyEventType: KeyEventType, cartAction: CartAction, pad: Pad): Unit = {
-		if (keyEventType eq KeyEventType.DOWN) {
-			pad.setStatus(PadStatus.RESTART)
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.scala b/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.scala
deleted file mode 100644
index 11957fa9729dc897f75c0aa43c9544f732ca5d01..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/action/actions/cart/handler/PlayStopHandler.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package de.tobias.playpad.action.actions.cart.handler
-
-import de.thecodelabs.midi.event.KeyEventType
-import de.tobias.playpad.action.actions.CartAction
-import de.tobias.playpad.pad.Pad
-
-class PlayStopHandler extends CartActionHandler {
-
-	override def performAction(keyEventType: KeyEventType, cartAction: CartAction, pad: Pad): Unit = {
-		if (keyEventType eq KeyEventType.DOWN) {
-			if (pad.isPlay) {
-				pad.stop()
-			} else { // Allow the listener to send the feedback
-				// cartAction.getPadPositionListener.setSend(false) TODO Fix
-				pad.play()
-			}
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala
deleted file mode 100644
index b639f9099419bd956a0b420216f9c01568b2c679..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernCartDesignHandlerImpl.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-package de.tobias.playpad.design
-
-import de.thecodelabs.utils.application.ApplicationUtils
-import de.tobias.playpad.design.modern.model.{ModernCartDesign, ModernGlobalDesign}
-import de.tobias.playpad.design.modern.{ModernCartDesignHandler, ModernColor}
-import de.tobias.playpad.pad.content.play.Durationable
-import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController
-import de.tobias.playpad.util.Minifier
-import de.tobias.playpad.view.PseudoClasses
-import javafx.util.Duration
-import org.springframework.expression.ExpressionParser
-import org.springframework.expression.common.TemplateParserContext
-import org.springframework.expression.spel.standard.SpelExpressionParser
-import org.springframework.expression.spel.support.StandardEvaluationContext
-
-import scala.jdk.CollectionConverters._
-
-class ModernCartDesignHandlerImpl extends ModernCartDesignHandler {
-
-	override def generateCss(design: ModernCartDesign, classSuffix: String, flat: Boolean): String = {
-		String.join(
-			generateCss(design, flat, classSuffix, design.getBackgroundColor),
-			generateCss(design, flat, classSuffix, design.getPlayColor, s":${PseudoClasses.PLAY_CLASS.getPseudoClassName}"),
-			generateCss(design, flat, classSuffix, design.getBackgroundColor, s":${PseudoClasses.WARN_CLASS.getPseudoClassName}")
-		)
-	}
-
-	private def generateCss(design: ModernCartDesign, flat: Boolean, padIdentifier: String, color: ModernColor, styleState: String = ""): String = {
-		val expressionParser: ExpressionParser = new SpelExpressionParser()
-		val context = new StandardEvaluationContext()
-
-		val resource = ApplicationUtils.getApplication.getClasspathResource("style/modern-pad.css")
-		val string = Minifier minify resource.getAsString
-
-		val values: Map[String, AnyRef] = Map(
-			"prefix" -> padIdentifier,
-			"class" -> styleState,
-			"buttonColor" -> color.getButtonColor,
-			"playbarTrackColor" -> color.getPlaybarColor,
-			"playbarBarColor" -> color.getPlaybarTrackColor,
-			"padColor" -> (if (flat) color.paint() else color.linearGradient()),
-			"padCueInColor" -> (if (flat) design.getCueInColor.paint() else design.getCueInColor.linearGradient()),
-			"fontColor" -> color.getFontColor
-		)
-
-		context.setVariables(values.asJava)
-		expressionParser.parseExpression(string, new TemplateParserContext("${", "}")).getValue(context, classOf[String])
-	}
-
-	override def handleWarning(design: ModernCartDesign, controller: AbstractPadViewController, warning: Duration, globalDesign: ModernGlobalDesign): Unit = {
-		if (globalDesign.isWarnAnimation) {
-			val playColor = design.getPlayColor
-			val backgroundColor = design.getBackgroundColor
-
-			val fadeStopColor = if (globalDesign.isFlatDesign) backgroundColor.toFlatFadeableColor else backgroundColor.toFadeableColor
-			val fadePlayColor = if (globalDesign.isFlatDesign) playColor.toFlatFadeableColor else playColor.toFadeableColor
-
-			val pad = controller.getPad
-			val animationDuration = pad.getContent match {
-				case durationable: Durationable =>
-					if (warning greaterThan durationable.getDuration) {
-						durationable.getDuration
-					} else {
-						warning
-					}
-				case _ => warning
-			}
-			ModernDesignAnimator.animateWarn(controller, fadePlayColor, fadeStopColor, animationDuration)
-		} else {
-			ModernDesignAnimator.warnFlash(controller)
-		}
-	}
-
-	override def stopWarning(design: ModernCartDesign, controller: AbstractPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller)
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala b/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala
deleted file mode 100644
index 3eea45b74878a931b801b72ea341ce13375be7d2..0000000000000000000000000000000000000000
--- a/PlayWall/src/main/scala/de/tobias/playpad/design/ModernGlobalDesignHandlerImpl.scala
+++ /dev/null
@@ -1,131 +0,0 @@
-package de.tobias.playpad.design
-
-import de.thecodelabs.utils.application.ApplicationUtils
-import de.thecodelabs.utils.application.container.PathType
-import de.tobias.playpad.design.modern.model.{ModernCartDesign, ModernGlobalDesign}
-import de.tobias.playpad.design.modern.{ModernColor, ModernGlobalDesignHandler}
-import de.tobias.playpad.pad.content.play.Durationable
-import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController
-import de.tobias.playpad.project.Project
-import de.tobias.playpad.util.Minifier
-import de.tobias.playpad.view.{ColorPickerView, PseudoClasses}
-import de.tobias.playpad.viewcontroller.main.IMainViewController
-import de.tobias.playpad.{DisplayableColor, PlayPadMain}
-import javafx.scene.paint.Color
-import javafx.stage.Stage
-import javafx.util.Duration
-import org.springframework.expression.ExpressionParser
-import org.springframework.expression.common.TemplateParserContext
-import org.springframework.expression.spel.standard.SpelExpressionParser
-import org.springframework.expression.spel.support.StandardEvaluationContext
-
-import java.nio.file.Files
-import java.util.function.Consumer
-import scala.jdk.CollectionConverters._
-
-class ModernGlobalDesignHandlerImpl extends ModernGlobalDesignHandler with ColorModeHandler {
-
-	override def applyStyleSheet(stage: Stage): Unit = {
-		stage.getScene.getStylesheets.add("style/style.css")
-		stage.getScene.getStylesheets.add("style/modern.css")
-
-		// Custom style for playwall available
-		val customCss = ApplicationUtils.getApplication.getPath(PathType.CONFIGURATION, "style.css")
-		if (Files exists customCss) {
-			stage.getScene.getStylesheets.add(customCss.toUri.toString)
-		}
-	}
-
-	override def applyStyleSheetToMainViewController(design: ModernGlobalDesign, controller: IMainViewController, stage: Stage, project: Project): Unit = {
-		applyStyleSheet(stage)
-
-		controller.setGridColor(Color.TRANSPARENT)
-
-		// generate dynamic style sheet
-		val customCss = ApplicationUtils.getApplication.getPath(PathType.CONFIGURATION, "custom_style.css")
-		val stringBuilder = new StringBuilder()
-		stringBuilder.append(generateCss(design))
-
-		val cartDesignHandler = PlayPadMain.getProgramInstance.getModernDesign.cart
-		project.getPage(controller.getPage).getPads.forEach(pad => {
-			val padSettings = pad.getPadSettings
-
-			if (padSettings.isCustomDesign) {
-				val cartDesign = padSettings.getDesign
-				stringBuilder.append(cartDesignHandler.generateCss(cartDesign, s"${pad.getPadIndex}", design.isFlatDesign))
-			}
-		})
-		Files.write(customCss, stringBuilder.toString().getBytes())
-
-		stage.getScene.getStylesheets.remove(customCss.toUri.toString)
-		stage.getScene.getStylesheets.add(customCss.toUri.toString)
-	}
-
-	private def generateCss(design: ModernGlobalDesign): String = {
-		String.join(
-			generateCss(design, design.getBackgroundColor),
-			generateCss(design, design.getPlayColor, s":${PseudoClasses.PLAY_CLASS.getPseudoClassName}"),
-			generateCss(design, design.getBackgroundColor, s":${PseudoClasses.WARN_CLASS.getPseudoClassName}")
-		)
-	}
-
-	private def generateCss(design: ModernGlobalDesign, color: ModernColor, styleState: String = ""): String = {
-		val expressionParser: ExpressionParser = new SpelExpressionParser()
-		val context = new StandardEvaluationContext()
-
-		val resource = ApplicationUtils.getApplication.getClasspathResource("style/modern-global.css")
-		val string = Minifier minify resource.getAsString
-
-		val values: Map[String, AnyRef] = Map(
-			"class" -> styleState,
-			"buttonColor" -> color.getButtonColor,
-			"playbarTrackColor" -> color.getPlaybarColor,
-			"playbarBarColor" -> color.getPlaybarTrackColor,
-			"padColor" -> (if (design.isFlatDesign) color.paint() else color.linearGradient()),
-			"padCueInColor" -> (if (design.isFlatDesign) design.getCueInColor.paint() else design.getCueInColor.linearGradient()),
-			"fontColor" -> color.getFontColor,
-			"infoFontSize" -> s"${design.getInfoFontSize}",
-			"titleFontSize" -> s"${design.getTitleFontSize}"
-		)
-
-		context.setVariables(values.asJava)
-		expressionParser.parseExpression(string, new TemplateParserContext("${", "}")).getValue(context, classOf[String])
-	}
-
-	override def handleWarning(design: ModernGlobalDesign, controller: AbstractPadViewController, warning: Duration): Unit = {
-		if (design.isWarnAnimation) {
-			warnAnimation(design, controller, warning)
-		} else {
-			ModernDesignAnimator.warnFlash(controller)
-		}
-	}
-
-	private def warnAnimation(design: ModernGlobalDesign, controller: AbstractPadViewController, warning: Duration): Unit = {
-		val stopColor = if (design.isFlatDesign) design.getBackgroundColor.toFlatFadeableColor else design.getBackgroundColor.toFadeableColor
-		val playColor = if (design.isFlatDesign) design.getPlayColor.toFlatFadeableColor else design.getPlayColor.toFadeableColor
-
-		val pad = controller.getPad
-		val duration = pad.getContent match {
-			case durationable: Durationable =>
-				if (warning.greaterThan(durationable.getDuration)) {
-					durationable.getDuration
-				} else {
-					warning
-				}
-			case _ => warning
-		}
-		ModernDesignAnimator.animateWarn(controller, playColor, stopColor, duration)
-	}
-
-	override def stopWarning(design: ModernGlobalDesign, controller: AbstractPadViewController): Unit = ModernDesignAnimator.stopAnimation(controller)
-
-	override def getColorInterface(onSelection: Consumer[DisplayableColor]) = new ColorPickerView(null, ModernColor.values.asInstanceOf[Array[DisplayableColor]], onSelection)
-
-	override def setColor(design: ModernCartDesign, color: DisplayableColor): Unit = {
-		color match {
-			case c: ModernColor =>
-				design.setBackgroundColor(c)
-			case _ =>
-		}
-	}
-}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/CheckUpdateTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/CheckUpdateTask.scala
index a9e68db3161f8184b972dea93a5cd82d4ea9a704..f5c6704b2f45be82bba39fad6b945adb023385ed 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/CheckUpdateTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/CheckUpdateTask.scala
@@ -1,7 +1,5 @@
 package de.tobias.playpad.initialize
 
-import java.io.IOException
-
 import de.thecodelabs.logger.Logger
 import de.thecodelabs.utils.application
 import de.thecodelabs.utils.threading.Worker
@@ -10,6 +8,8 @@ import de.tobias.playpad.viewcontroller.dialog.AutoUpdateDialog
 import javafx.application.Platform
 import javafx.scene.control.ButtonType
 
+import java.io.IOException
+
 class CheckUpdateTask extends PlayPadInitializeTask {
 	override def name(): String = "Updates"
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ListenerRegistrationTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ListenerRegistrationTask.scala
new file mode 100644
index 0000000000000000000000000000000000000000..5ab0117dad7486c416596fcc3d2c13952d808cb8
--- /dev/null
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ListenerRegistrationTask.scala
@@ -0,0 +1,13 @@
+package de.tobias.playpad.initialize
+
+import de.thecodelabs.utils.application
+import de.tobias.playpad.PlayPadImpl
+import de.tobias.playpad.trigger.VolumeTriggerVolumeFilter
+
+class ListenerRegistrationTask extends PlayPadInitializeTask {
+	override def name(): String = "ListenerRegistration"
+
+	override def run(app: application.App, instance: PlayPadImpl): Unit = {
+		instance.addPadListener(VolumeTriggerVolumeFilter.getInstance())
+	}
+}
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/MidiActionsInitializerTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/MidiActionsInitializerTask.scala
index fab6bfe1c728a1edd52772cad91b767b95cc40e2..7ca988c7db723dd776e3fc3ade467bbd13200e5f 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/MidiActionsInitializerTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/MidiActionsInitializerTask.scala
@@ -1,10 +1,11 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.midi.action.{ActionKeyHandler, ActionRegistry}
 import de.thecodelabs.midi.midi.MidiCommandHandler
 import de.thecodelabs.utils.application
-import de.tobias.playpad.{PlayPadImpl, PlayPadPlugin}
 import de.tobias.playpad.action.ActionProvider
 import de.tobias.playpad.midi.PD12
+import de.tobias.playpad.{PlayPadImpl, PlayPadPlugin}
 
 class MidiActionsInitializerTask extends PlayPadInitializeTask {
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/NativeAppInitializerTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/NativeAppInitializerTask.scala
index 5afd167f0dc959eb7ac44277ea4e5b9d5acab202..96b15a5dd8aedca5c8be678ad84926d0d6ad991c 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/NativeAppInitializerTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/NativeAppInitializerTask.scala
@@ -1,4 +1,5 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.utils.application
 import de.thecodelabs.utils.application.system.NativeApplication
 import de.tobias.playpad.PlayPadImpl
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/OpenLastDocumentTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/OpenLastDocumentTask.scala
index c518daba0cd81d8a5465ee35f2f89b8d7d51c978..1d3b58cc5d22bc912f93c6de9ea289f2bd183ec9 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/OpenLastDocumentTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/OpenLastDocumentTask.scala
@@ -1,12 +1,12 @@
 package de.tobias.playpad.initialize
 
-import java.util.UUID
-
 import de.thecodelabs.utils.application
 import de.tobias.playpad.PlayPadImpl
 import de.tobias.playpad.project.ref.ProjectReferenceManager
 import javafx.application.Platform
 
+import java.util.UUID
+
 class OpenLastDocumentTask extends PlayPadInitializeTask {
 	override def name(): String = "Open Last Document"
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/PluginLoadingTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/PluginLoadingTask.scala
index 7b4d7bacf7a75ff8a6ee3ad8e67cd9a523ed6ffc..04ab9448cfae14e988a7701da0ea583e131b1733 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/PluginLoadingTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/PluginLoadingTask.scala
@@ -1,8 +1,5 @@
 package de.tobias.playpad.initialize
 
-import java.io.IOException
-import java.nio.file.{Path, Paths}
-
 import de.thecodelabs.logger.Logger
 import de.thecodelabs.utils.application
 import de.thecodelabs.utils.application.ApplicationUtils
@@ -12,6 +9,9 @@ import de.tobias.playpad.plugin.ModernPluginManager
 import javafx.application.Platform
 import org.controlsfx.dialog.ExceptionDialog
 
+import java.io.IOException
+import java.nio.file.{Path, Paths}
+
 class PluginLoadingTask extends PlayPadInitializeTask {
 	override def name(): String = "Plugins"
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProfileLoadingTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProfileLoadingTask.scala
index c6f3b28b77789c34d7ab32e3d7e4c3161a457409..490bddc2ed93996df80b17d08a25657d91ca314d 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProfileLoadingTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProfileLoadingTask.scala
@@ -1,13 +1,13 @@
 package de.tobias.playpad.initialize
 
-import java.io.IOException
-
 import de.thecodelabs.logger.Logger
 import de.thecodelabs.utils.application
 import de.tobias.playpad.PlayPadImpl
 import de.tobias.playpad.profile.ref.ProfileReferenceManager
 import org.dom4j.DocumentException
 
+import java.io.IOException
+
 class ProfileLoadingTask extends PlayPadInitializeTask {
 	override def name(): String = "Profile loading"
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectParameterOpenTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectParameterOpenTask.scala
index a659cbc9bc55ffca0883c8dc824f0af23ca1a609..7a37af3e49bce98f4b3c63145354db9172cad3d7 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectParameterOpenTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectParameterOpenTask.scala
@@ -1,11 +1,10 @@
 package de.tobias.playpad.initialize
 
-import java.util.UUID
-
 import de.thecodelabs.utils.application
 import de.tobias.playpad.PlayPadImpl
 import de.tobias.playpad.project.ref.ProjectReferenceManager
-;
+
+import java.util.UUID;
 
 class ProjectParameterOpenTask extends PlayPadInitializeTask {
 	override def name(): String = "Open"
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectsLoadingTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectsLoadingTask.scala
index b2bc99af6d57599077d177dd93a8e732b9606132..4609a499f17254359e38d0e5764ff18e6a2473e0 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectsLoadingTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ProjectsLoadingTask.scala
@@ -1,4 +1,5 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.utils.application
 import de.tobias.playpad.PlayPadImpl
 import de.tobias.playpad.project.ref.ProjectReferenceManager
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServerInitializeTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServerInitializeTask.scala
index a55c2ad0272c3176d8f8b0da730269b4bc23a9c3..374708dbf113f87f1c002024b89d2ef1473fe99c 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServerInitializeTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServerInitializeTask.scala
@@ -1,13 +1,13 @@
 package de.tobias.playpad.initialize
 
-import java.io.IOException
-
 import com.neovisionaries.ws.client.WebSocketException
 import de.thecodelabs.logger.Logger
 import de.thecodelabs.utils.application
 import de.tobias.playpad.server.{Session, SessionDelegate, SessionNotExistsException}
 import de.tobias.playpad.{PlayPadImpl, PlayPadPlugin}
 
+import java.io.IOException
+
 class ServerInitializeTask(delegate: SessionDelegate) extends PlayPadInitializeTask {
 	override def name(): String = "Server"
 
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServiceInitializationTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServiceInitializationTask.scala
index 7c7795ee81249b886ce185cd11ff51a3de2cbb52..30dee76bd1c7082fbf9ba30c17448ed0259520ea 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServiceInitializationTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/ServiceInitializationTask.scala
@@ -1,4 +1,5 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.utils.application
 import de.tobias.playpad.server.ServerHandlerImpl
 import de.tobias.playpad.server.sync.command.CommandExecutorHandlerImpl
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/VersionizerSetupTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/VersionizerSetupTask.scala
index 3c78ccb991a479e5df94f17e0b0efd6320d2f1fb..7a5f95697a0a78f3ef4d58dc4c631b7e28339f48 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/VersionizerSetupTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/VersionizerSetupTask.scala
@@ -1,4 +1,5 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.storage.settings.StorageTypes
 import de.thecodelabs.utils.application
 import de.thecodelabs.utils.util.SystemUtils
diff --git a/PlayWall/src/main/scala/de/tobias/playpad/initialize/VolumeInitializerTask.scala b/PlayWall/src/main/scala/de/tobias/playpad/initialize/VolumeInitializerTask.scala
index a09ddf7f3f9bbd541478bbd6f6c16b232f1129bf..4018cba0446f88b295d7ae357c88c43d91b205a4 100644
--- a/PlayWall/src/main/scala/de/tobias/playpad/initialize/VolumeInitializerTask.scala
+++ b/PlayWall/src/main/scala/de/tobias/playpad/initialize/VolumeInitializerTask.scala
@@ -1,4 +1,5 @@
 package de.tobias.playpad.initialize
+
 import de.thecodelabs.utils.application
 import de.tobias.playpad.PlayPadImpl
 import de.tobias.playpad.volume.{GlobalVolume, PadVolume, VolumeManager}
diff --git a/PlayWall/src/test/java/de/tobias/playpad/update/VersionUpdaterTest.java b/PlayWall/src/test/java/de/tobias/playpad/update/VersionUpdaterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3161ec958483f5dad5406ae20de2a8043f4203e
--- /dev/null
+++ b/PlayWall/src/test/java/de/tobias/playpad/update/VersionUpdaterTest.java
@@ -0,0 +1,43 @@
+package de.tobias.playpad.update;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.junit.Test;
+
+import java.io.InputStream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class VersionUpdaterTest {
+	@Test
+	public void testMigrateProject_UpdatePadDesignSettings() throws DocumentException {
+		final InputStream projectInputStream = VersionUpdaterTest.class.getClassLoader().getResourceAsStream("de/tobias/playpad/update/ProjectToMigrateToVersion44.xml");
+		final Document migratedProject = VersionUpdater.updateProject(projectInputStream);
+
+		final Element rootElement = migratedProject.getRootElement();
+		final Element pageElement = rootElement.elements().get(0);
+
+		// pad with custom design enabled
+		final Element padElement = pageElement.elements().get(0);
+		final Element designElement = padElement.element("Settings").element("Design");
+
+		assertThat(designElement.attribute("custom")).isNull();
+
+		assertThat(designElement.element("EnableCustomBackgroundColor").getStringValue()).isEqualTo("true");
+		assertThat(designElement.element("BackgroundColor").getStringValue()).isEqualTo("RED2");
+
+		assertThat(designElement.element("EnableCustomPlayColor").getStringValue()).isEqualTo("true");
+		assertThat(designElement.element("PlayColor").getStringValue()).isEqualTo("YELLOW2");
+
+		assertThat(designElement.element("EnableCustomCueInColor").getStringValue()).isEqualTo("true");
+		assertThat(designElement.element("CueInColor").getStringValue()).isEqualTo("LIGHT_GREEN2");
+
+
+		// pad without custom design
+		final Element padElement2 = pageElement.elements().get(1);
+		final Element designElement2 = padElement2.element("Settings").element("Design");
+
+		assertThat(designElement2.attribute("custom")).isNull();
+	}
+}
diff --git a/PlayWall/src/test/java/de/tobias/playpad/viewcontroller/LaunchDialogTest.java b/PlayWall/src/test/java/de/tobias/playpad/viewcontroller/LaunchDialogTest.java
index e0c6cd130d60b3a91b07231ae7357f80333470d8..7ac926ec621079a2d1552ebbc9d8bef6544ba6d1 100644
--- a/PlayWall/src/test/java/de/tobias/playpad/viewcontroller/LaunchDialogTest.java
+++ b/PlayWall/src/test/java/de/tobias/playpad/viewcontroller/LaunchDialogTest.java
@@ -21,7 +21,7 @@ public class LaunchDialogTest extends ApplicationTest {
 	@Before
 	public void init() {
 		App app = ApplicationUtils.registerMainApplication(PlayPadMain.class);
-		ApplicationUtils.registerUpdateSercive(new VersionUpdater());
+		ApplicationUtils.registerUpdateService(new VersionUpdater());
 
 		Logger.init(app.getPath(PathType.LOG));
 		Logger.setLevelFilter(LogLevelFilter.DEBUG);
diff --git a/PlayWall/src/test/resources/de/tobias/playpad/update/ProjectToMigrateToVersion44.xml b/PlayWall/src/test/resources/de/tobias/playpad/update/ProjectToMigrateToVersion44.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86a23a31d6079ae067eb9329594ba46cc50d65d9
--- /dev/null
+++ b/PlayWall/src/test/resources/de/tobias/playpad/update/ProjectToMigrateToVersion44.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<Project>
+    <Page uuid="a40033c7-4a85-49c9-bdfe-00e0e8a35fa2" id="0" name="">
+        <Pad uuid="1028ee71-aebb-4975-8bfd-40d6872bc1b0" index="0" name="" status="EMPTY">
+            <Settings id="cc73d33f-e3dc-4b58-b199-2a77577b1d89">
+                <Volume>1.0</Volume>
+                <Loop>false</Loop>
+                <Design id="5603c0a3-f3e6-4191-857b-978ba3846e28" custom="true">
+                    <BackgroundColor>RED2</BackgroundColor>
+                    <PlayColor>YELLOW2</PlayColor>
+                    <CueInColor>LIGHT_GREEN2</CueInColor>
+                </Design>
+                <CustomSettings/>
+                <Triggers>
+                    <Trigger point="START"/>
+                    <Trigger point="EOF_STOP"/>
+                </Triggers>
+            </Settings>
+        </Pad>
+        <Pad uuid="bde56734-a606-4c19-a19b-7ef20aa7b827" index="1" name="" status="EMPTY">
+            <Settings id="0be18dbe-f914-4a11-a470-b4279fe98e0f">
+                <Volume>1.0</Volume>
+                <Loop>false</Loop>
+                <Design custom="false"/>
+                <CustomSettings/>
+                <Triggers>
+                    <Trigger point="START"/>
+                    <Trigger point="EOF_STOP"/>
+                </Triggers>
+            </Settings>
+        </Pad>
+    </Page>
+    <Settings>
+        <Columns>2</Columns>
+        <Rows>1</Rows>
+        <MediaPath active="false"/>
+    </Settings>
+</Project>
diff --git a/PlayWallComponents/pom.xml b/PlayWallComponents/pom.xml
index 014e6b86de1ac4bfcd3f2e4f69af6c276f1567f9..294c91b4e93d28cb1a77a0cf9135c618ed9e0abc 100644
--- a/PlayWallComponents/pom.xml
+++ b/PlayWallComponents/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallDesktop</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallComponents</artifactId>
diff --git a/PlayWallCore/pom.xml b/PlayWallCore/pom.xml
index e88205b628d36d178dd5d0938e379c960fd4d7ea..eb5dbc6e6e2a1679abac09b8ca6f43693f5233db 100644
--- a/PlayWallCore/pom.xml
+++ b/PlayWallCore/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallDesktop</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallCore</artifactId>
@@ -122,6 +122,12 @@
             <version>${jna.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>net.sf.jni4net</groupId>
+            <artifactId>jni4net.j</artifactId>
+            <version>${jni4net.j.version}</version>
+        </dependency>
+
         <!--Tools-->
         <dependency>
             <groupId>org.scala-lang</groupId>
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/PlayPad.java b/PlayWallCore/src/main/java/de/tobias/playpad/PlayPad.java
index c480828c5d48c0712eab670dbd4a3d2ed05ede89..b71e1386c601f744a46f4287b1df93ca2ebb3859 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/PlayPad.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/PlayPad.java
@@ -107,6 +107,13 @@ public interface PlayPad {
 	 */
 	Image getIcon();
 
+	/**
+	 * Gibt das Programm Icon als byte[] zurück.
+	 *
+	 * @return Icon
+	 */
+	byte[] getIconData();
+
 	/**
 	 * Beendet PlayWall.
 	 */
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/ColorAdjuster.java b/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/ColorAdjuster.java
index 43b68616b2f60bfe777cb52cef208acb88f6ea2a..3c43cca588470edbbd0f989788a8366e11cbb329 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/ColorAdjuster.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/ColorAdjuster.java
@@ -12,6 +12,7 @@ import de.thecodelabs.midi.mapping.MidiKey;
 import de.thecodelabs.midi.midi.Midi;
 import de.thecodelabs.midi.midi.feedback.MidiFeedbackTranscript;
 import de.tobias.playpad.design.FeedbackDesignColorSuggester;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.profile.Profile;
 import javafx.scene.paint.Color;
@@ -69,19 +70,20 @@ public class ColorAdjuster {
 
 		Pad pad = suggester.getPad(action);
 
-		Color layoutStdColor = null;
-		Color layoutEvColor = null;
+		FeedbackDesignColorSuggester globalDesign = Profile.currentProfile().getProfileSettings().getDesign();
+		Color layoutStdColor = globalDesign.getDesignDefaultColor();
+		Color layoutEvColor = globalDesign.getDesignEventColor();
 
-		FeedbackDesignColorSuggester design;
-		if (pad != null && pad.getPadSettings().isCustomDesign()) {
-			design = pad.getPadSettings().getDesign();
-		} else {
-			design = Profile.currentProfile().getProfileSettings().getDesign();
-		}
+		if (pad != null) {
+			final ModernCartDesign padDesign = pad.getPadSettings().getDesign();
+
+			if (padDesign.isEnableCustomBackgroundColor()) {
+				layoutStdColor = padDesign.getDesignDefaultColor();
+			}
 
-		if (design != null) {
-			layoutStdColor = design.getDesignDefaultColor();
-			layoutEvColor = design.getDesignEventColor();
+			if (padDesign.isEnableCustomPlayColor()) {
+				layoutEvColor = padDesign.getDesignEventColor();
+			}
 		}
 
 		if (layoutStdColor != null) {
@@ -96,9 +98,7 @@ public class ColorAdjuster {
 				final byte channel = suggester.suggestFeedbackChannel(FeedbackType.EVENT);
 				key.setEventFeedback(new Feedback(channel, matchedColor.getValue()));
 			});
-		}
 
-		if (layoutEvColor != null) {
 			searchColor(transcript, layoutEvColor).ifPresent(matchedColor -> {
 				final byte channel = suggester.suggestFeedbackChannel(FeedbackType.WARNING);
 				key.setWarningFeedback(new Feedback(channel, matchedColor.getValue()));
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/FeedbackColorSuggester.java b/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/FeedbackColorSuggester.java
index 9af2b7699149cbf75ea4ed13c3ac872b224ccd05..53aa94a8d940e4e45b50fe15854bb29c808fa14c 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/FeedbackColorSuggester.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/FeedbackColorSuggester.java
@@ -3,7 +3,11 @@ package de.tobias.playpad.action.feedback;
 import de.thecodelabs.midi.feedback.FeedbackColor;
 import javafx.scene.paint.Color;
 
+import java.util.List;
+
 public interface FeedbackColorSuggester {
 
+	List<String> getMidiColorMappings();
+
 	FeedbackColor suggest(Color color);
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/LightMode.java b/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/LightMode.java
deleted file mode 100644
index 17542c04334d2b3ba848a0cc87e587a71eb5c1c4..0000000000000000000000000000000000000000
--- a/PlayWallCore/src/main/java/de/tobias/playpad/action/feedback/LightMode.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package de.tobias.playpad.action.feedback;
-
-import de.thecodelabs.midi.feedback.FeedbackColor;
-
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-public enum LightMode {
-	LOW,
-	MIDDLE,
-	NORMAL,
-	HIGH;
-
-	public interface ILightMode extends FeedbackColor {
-		LightMode getLightMode();
-
-		FeedbackColor translate(LightMode lightMode);
-	}
-
-	public static List<ILightMode> filter(ILightMode[] values, LightMode filter) {
-		return Stream.of(values)
-				.filter(v -> v.getLightMode() == filter)
-				.collect(Collectors.toList());
-	}
-}
\ No newline at end of file
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignProvider.java
similarity index 61%
rename from PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignHandler.java
rename to PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignProvider.java
index a96e7a9bf1223c41a82a522f03f0a2ebc1769f04..6b374d257db4b5dc8b09ef944bc0ad9d98eaf0fd 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignHandler.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/ModernDesignProvider.java
@@ -2,8 +2,12 @@ package de.tobias.playpad.design;
 
 import de.tobias.playpad.design.modern.ModernCartDesignHandler;
 import de.tobias.playpad.design.modern.ModernGlobalDesignHandler;
+import de.tobias.playpad.design.modern.ModernWarningDesignHandler;
+
+public interface ModernDesignProvider {
+
+	ModernWarningDesignHandler warning();
 
-public interface ModernDesignHandler {
 	ModernGlobalDesignHandler global();
 
 	ModernCartDesignHandler cart();
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java
index 200ead1fd6e412137370b6393167bb1092adc90d..f456ca512fd0dc06d505a13ca0fa071d0be113ee 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernCartDesignHandler.java
@@ -1,20 +1,7 @@
 package de.tobias.playpad.design.modern;
 
 import de.tobias.playpad.design.modern.model.ModernCartDesign;
-import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
-import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
-import javafx.util.Duration;
 
 public interface ModernCartDesignHandler {
-
 	String generateCss(ModernCartDesign design, String classSuffix, boolean flat);
-
-	/*
-	 * Wird in einem neuen Thread aufgerufen
-	 */
-	void handleWarning(ModernCartDesign design, AbstractPadViewController controller, Duration warning, ModernGlobalDesign globalDesign);
-
-	default void stopWarning(ModernCartDesign design, AbstractPadViewController controller) {
-	}
-
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java
index bca1e4aa9cee672852326f41dffe582e115f10e4..bd7e9348e4b1b92315b9c094e19968e360a46835 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernGlobalDesignHandler.java
@@ -1,23 +1,13 @@
 package de.tobias.playpad.design.modern;
 
 import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
-import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.viewcontroller.main.IMainViewController;
 import javafx.stage.Stage;
-import javafx.util.Duration;
 
 public interface ModernGlobalDesignHandler {
 
 	void applyStyleSheet(Stage stage);
 
 	void applyStyleSheetToMainViewController(ModernGlobalDesign design, IMainViewController controller, Stage stage, Project project);
-
-	/*
-	 * Wird in einem neuen Thread aufgerufen
-	 */
-	void handleWarning(ModernGlobalDesign design, AbstractPadViewController controller, Duration warning);
-
-	default void stopWarning(ModernGlobalDesign design, AbstractPadViewController controller) {
-	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernWarningDesignHandler.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernWarningDesignHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..405b60e0c3b87a862005bd23501e8ad9d4005643
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/ModernWarningDesignHandler.java
@@ -0,0 +1,51 @@
+package de.tobias.playpad.design.modern;
+
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
+import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.content.play.Durationable;
+import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
+import de.tobias.playpad.project.api.IPad;
+import de.tobias.playpad.util.FadeableColor;
+import javafx.util.Duration;
+
+public interface ModernWarningDesignHandler {
+
+	default void handleWarning(ModernGlobalDesign globalDesign, ModernCartDesign cartDesign, AbstractPadViewController controller, Duration warningDuration) {
+		ModernColor backgroundColor = globalDesign.getBackgroundColor();
+		ModernColor playColor = globalDesign.getPlayColor();
+
+		if (cartDesign.isEnableCustomBackgroundColor()) {
+			backgroundColor = cartDesign.getBackgroundColor();
+		}
+
+		if (cartDesign.isEnableCustomPlayColor()) {
+			playColor = cartDesign.getPlayColor();
+		}
+
+		final FadeableColor fadeStartColor = globalDesign.isFlatDesign() ? playColor.toFlatFadeableColor() : playColor.toFadeableColor();
+		final FadeableColor fadeStopColor = globalDesign.isFlatDesign() ? backgroundColor.toFlatFadeableColor() : backgroundColor.toFadeableColor();
+		final Duration duration = determineDuration(controller.getPad(), warningDuration);
+
+		performWarning(globalDesign, fadeStartColor, fadeStopColor, controller, duration);
+	}
+
+	default Duration determineDuration(Pad pad, Duration warningDuration) {
+		if (pad.getContent() instanceof Durationable) {
+			final Durationable durationable = (Durationable) pad.getContent();
+			if (warningDuration.greaterThan(durationable.getDuration())) {
+				return durationable.getDuration();
+			}
+		}
+
+		return warningDuration;
+	}
+
+	/*
+	 * Wird in einem neuen Thread aufgerufen
+	 */
+	void performWarning(ModernGlobalDesign design, FadeableColor fadeStartColor, FadeableColor fadeStopColor, AbstractPadViewController controller, Duration duration);
+
+	default void stopWarning(IPad pad) {
+	}
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/model/ModernCartDesign.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/model/ModernCartDesign.java
index 0d79598ab08bdc44c125ee494909fe9925942abc..0997d291bb879d0820829479946966374827d659 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/model/ModernCartDesign.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/model/ModernCartDesign.java
@@ -6,7 +6,9 @@ import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.server.sync.command.CommandManager;
 import de.tobias.playpad.server.sync.command.Commands;
 import de.tobias.playpad.server.sync.listener.upstream.DesignUpdateListener;
+import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.scene.paint.Color;
 
@@ -14,28 +16,87 @@ import java.util.UUID;
 
 public class ModernCartDesign implements FeedbackDesignColorSuggester {
 
-	private UUID uuid;
+	public static class ModernCartDesignBuilder {
+		private final Pad pad;
+		private final UUID id;
+
+		private ModernColor backgroundColor = DEFAULT_COLOR_BACKGROUND;
+		private Boolean enableCustomBackgroundColor = false;
+
+		private ModernColor playColor = DEFAULT_COLOR_PLAY;
+		private Boolean enableCustomPlayColor = false;
+
+		private ModernColor cueInColor = DEFAULT_COLOR_CUE_IN;
+		private Boolean enableCustomCueInColor = false;
+
+		public ModernCartDesignBuilder(Pad pad) {
+			this.pad = pad;
+			this.id = UUID.randomUUID();
+		}
+
+		public ModernCartDesignBuilder(Pad pad, UUID id) {
+			this.pad = pad;
+			this.id = id;
+		}
+
+		public ModernCartDesignBuilder withBackgroundColor(ModernColor backgroundColor, Boolean enable) {
+			this.backgroundColor = backgroundColor;
+			this.enableCustomBackgroundColor = enable;
+			return this;
+		}
+
+		public ModernCartDesignBuilder withPlayColor(ModernColor playColor, Boolean enable) {
+			this.playColor = playColor;
+			this.enableCustomPlayColor = enable;
+			return this;
+		}
+
+		public ModernCartDesignBuilder withCueInColor(ModernColor cueInColor, Boolean enable) {
+			this.cueInColor = cueInColor;
+			this.enableCustomCueInColor = enable;
+			return this;
+		}
+
+		public ModernCartDesign build() {
+			return new ModernCartDesign(pad, id,
+					backgroundColor, enableCustomBackgroundColor,
+					playColor, enableCustomPlayColor,
+					cueInColor, enableCustomCueInColor);
+		}
+	}
+
+	public static final ModernColor DEFAULT_COLOR_BACKGROUND = ModernColor.GRAY1;
+	public static final ModernColor DEFAULT_COLOR_PLAY = ModernColor.RED3;
+	public static final ModernColor DEFAULT_COLOR_CUE_IN = ModernColor.RED2;
+
+	private final Pad pad;
+	private final UUID uuid;
+
+	private BooleanProperty enableCustomBackgroundColor;
 	private ObjectProperty<ModernColor> backgroundColor;
+
+	private BooleanProperty enableCustomPlayColor;
 	private ObjectProperty<ModernColor> playColor;
+
+	private BooleanProperty enableCustomCueInColor;
 	private ObjectProperty<ModernColor> cueInColor;
 
-	private Pad pad;
 	private DesignUpdateListener syncListener;
 
-	public ModernCartDesign(Pad pad) {
-		this(pad, UUID.randomUUID());
-	}
-
-	public ModernCartDesign(Pad pad, UUID uuid) {
-		this(pad, uuid, ModernColor.GRAY1, ModernColor.RED3, ModernColor.RED2);
-	}
-
-	public ModernCartDesign(Pad pad, UUID id, ModernColor backgroundColor, ModernColor playColor, ModernColor cueInColor) {
+	private ModernCartDesign(Pad pad, UUID id,
+							 ModernColor backgroundColor, Boolean enableCustomBackgroundColor,
+							 ModernColor playColor, Boolean enableCustomPlayColor,
+							 ModernColor cueInColor, Boolean enableCustomCueInColor) {
 		this.uuid = id;
 		this.pad = pad;
 
+		this.enableCustomBackgroundColor = new SimpleBooleanProperty(enableCustomBackgroundColor);
 		this.backgroundColor = new SimpleObjectProperty<>(backgroundColor);
+
+		this.enableCustomPlayColor = new SimpleBooleanProperty(enableCustomPlayColor);
 		this.playColor = new SimpleObjectProperty<>(playColor);
+
+		this.enableCustomCueInColor = new SimpleBooleanProperty(enableCustomCueInColor);
 		this.cueInColor = new SimpleObjectProperty<>(cueInColor);
 
 		syncListener = new DesignUpdateListener(this);
@@ -49,6 +110,18 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 		return pad;
 	}
 
+	public boolean isEnableCustomBackgroundColor() {
+		return enableCustomBackgroundColor.get();
+	}
+
+	public void setEnableCustomBackgroundColor(Boolean enableCustomBackgroundColor) {
+		this.enableCustomBackgroundColor.set(enableCustomBackgroundColor);
+	}
+
+	public BooleanProperty enableCustomBackgroundColorProperty() {
+		return enableCustomBackgroundColor;
+	}
+
 	public ModernColor getBackgroundColor() {
 		return backgroundColor.get();
 	}
@@ -61,6 +134,18 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 		return backgroundColor;
 	}
 
+	public boolean isEnableCustomPlayColor() {
+		return enableCustomPlayColor.get();
+	}
+
+	public void setEnableCustomPlayColor(boolean enableCustomPlayColor) {
+		this.enableCustomPlayColor.set(enableCustomPlayColor);
+	}
+
+	public BooleanProperty enableCustomPlayColorProperty() {
+		return enableCustomPlayColor;
+	}
+
 	public ModernColor getPlayColor() {
 		return playColor.get();
 	}
@@ -73,6 +158,18 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 		return playColor;
 	}
 
+	public boolean isEnableCustomCueInColor() {
+		return enableCustomCueInColor.get();
+	}
+
+	public void setEnableCustomCueInColor(boolean enableCustomCueInColor) {
+		this.enableCustomCueInColor.set(enableCustomCueInColor);
+	}
+
+	public BooleanProperty enableCustomCueInColorProperty() {
+		return enableCustomCueInColor;
+	}
+
 	public ModernColor getCueInColor() {
 		return cueInColor.get();
 	}
@@ -94,13 +191,12 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 	}
 
 	public void reset() {
-		backgroundColor.set(ModernColor.GRAY1);
-		playColor.set(ModernColor.RED3);
-		cueInColor.set(ModernColor.RED2);
+		backgroundColor.set(DEFAULT_COLOR_BACKGROUND);
+		playColor.set(DEFAULT_COLOR_PLAY);
+		cueInColor.set(DEFAULT_COLOR_CUE_IN);
 	}
 
-
-	// Color Associator
+	// Color Associate
 	@Override
 	public Color getDesignEventColor() {
 		return Color.web(playColor.get().getColorHi());
@@ -112,14 +208,16 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 	}
 
 	public ModernCartDesign copy(Pad pad) {
-		ModernCartDesign clone = new ModernCartDesign(pad);
+		ModernCartDesign clone = new ModernCartDesignBuilder(pad).build();
 
+		clone.enableCustomBackgroundColor = new SimpleBooleanProperty(isEnableCustomBackgroundColor());
 		clone.backgroundColor = new SimpleObjectProperty<>(getBackgroundColor());
+
+		clone.enableCustomPlayColor = new SimpleBooleanProperty(isEnableCustomPlayColor());
 		clone.playColor = new SimpleObjectProperty<>(getPlayColor());
-		clone.cueInColor = new SimpleObjectProperty<>(getCueInColor());
 
-		clone.pad = pad;
-		clone.uuid = UUID.randomUUID();
+		clone.enableCustomCueInColor = new SimpleBooleanProperty(isEnableCustomCueInColor());
+		clone.cueInColor = new SimpleObjectProperty<>(getCueInColor());
 
 		syncListener = new DesignUpdateListener(clone);
 		if (pad.getProject().getProjectReference().isSync()) {
@@ -129,10 +227,4 @@ public class ModernCartDesign implements FeedbackDesignColorSuggester {
 
 		return clone;
 	}
-
-	public void copyGlobalLayout(ModernGlobalDesign globalDesign) {
-		setBackgroundColor(globalDesign.getBackgroundColor());
-		setPlayColor(globalDesign.getPlayColor());
-		setCueInColor(globalDesign.getCueInColor());
-	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializer.java b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializer.java
index ce67905eb387db419410d0598eb04264f937b270..279347a6b281505606cf61770ce7cc3ca192acfd 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializer.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializer.java
@@ -14,9 +14,18 @@ public class ModernCartDesignSerializer {
 		ModernCartDesign design;
 		String uuidValue = rootElement.attributeValue("id");
 		if (uuidValue != null) {
-			design = new ModernCartDesign(pad, UUID.fromString(uuidValue));
+			design = new ModernCartDesign.ModernCartDesignBuilder(pad, UUID.fromString(uuidValue)).build();
 		} else {
-			design = new ModernCartDesign(pad);
+			design = new ModernCartDesign.ModernCartDesignBuilder(pad).build();
+		}
+
+		Element enableCustomBackgroundColorElement = rootElement.element("EnableCustomBackgroundColor");
+		if (enableCustomBackgroundColorElement != null) {
+			try {
+				design.setEnableCustomBackgroundColor(Boolean.parseBoolean(enableCustomBackgroundColorElement.getStringValue()));
+			} catch (IllegalArgumentException e) {
+				Logger.error(e);
+			}
 		}
 
 		Element backgroundElement = rootElement.element("BackgroundColor");
@@ -28,6 +37,15 @@ public class ModernCartDesignSerializer {
 			}
 		}
 
+		Element enableCustomPlayColorElement = rootElement.element("EnableCustomPlayColor");
+		if (enableCustomPlayColorElement != null) {
+			try {
+				design.setEnableCustomPlayColor(Boolean.parseBoolean(enableCustomPlayColorElement.getStringValue()));
+			} catch (IllegalArgumentException e) {
+				Logger.error(e);
+			}
+		}
+
 		Element playElement = rootElement.element("PlayColor");
 		if (playElement != null) {
 			try {
@@ -36,6 +54,16 @@ public class ModernCartDesignSerializer {
 				Logger.error(e);
 			}
 		}
+
+		Element enableCustomCueInColorElement = rootElement.element("EnableCustomCueInColor");
+		if (enableCustomCueInColorElement != null) {
+			try {
+				design.setEnableCustomCueInColor(Boolean.parseBoolean(enableCustomCueInColorElement.getStringValue()));
+			} catch (IllegalArgumentException e) {
+				Logger.error(e);
+			}
+		}
+
 		Element cueInElement = rootElement.element("CueInColor");
 		if (cueInElement != null) {
 			try {
@@ -49,8 +77,14 @@ public class ModernCartDesignSerializer {
 
 	public void save(Element rootElement, ModernCartDesign design) {
 		rootElement.addAttribute("id", design.getId().toString());
+
+		rootElement.addElement("EnableCustomBackgroundColor").addText(String.valueOf(design.isEnableCustomBackgroundColor()));
 		rootElement.addElement("BackgroundColor").addText(design.getBackgroundColor().name());
+
+		rootElement.addElement("EnableCustomPlayColor").addText(String.valueOf(design.isEnableCustomPlayColor()));
 		rootElement.addElement("PlayColor").addText(design.getPlayColor().name());
+
+		rootElement.addElement("EnableCustomCueInColor").addText(String.valueOf(design.isEnableCustomCueInColor()));
 		rootElement.addElement("CueInColor").addText(design.getCueInColor().name());
 	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java
index cbefe4d2a0c9c95ce5607e7bcd510b7311212ea9..73f60d57b6508e359fe2e29260932804f1696d19 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/Pad.java
@@ -4,18 +4,22 @@ import de.thecodelabs.utils.io.PathUtils;
 import de.tobias.playpad.PlayPadPlugin;
 import de.tobias.playpad.pad.content.PadContent;
 import de.tobias.playpad.pad.content.PadContentFactory;
+import de.tobias.playpad.pad.content.Playlistable;
 import de.tobias.playpad.pad.content.play.Pauseable;
 import de.tobias.playpad.pad.fade.listener.PadFadeContentListener;
 import de.tobias.playpad.pad.fade.listener.PadFadeDurationListener;
+import de.tobias.playpad.pad.listener.PadNameChangeListener;
 import de.tobias.playpad.pad.listener.PadStatusControlListener;
 import de.tobias.playpad.pad.listener.PadStatusNotFoundListener;
 import de.tobias.playpad.pad.listener.trigger.PadTriggerContentListener;
 import de.tobias.playpad.pad.listener.trigger.PadTriggerDurationListener;
+import de.tobias.playpad.pad.listener.trigger.PadTriggerPlaylistListener;
 import de.tobias.playpad.pad.listener.trigger.PadTriggerStatusListener;
 import de.tobias.playpad.pad.mediapath.MediaPath;
 import de.tobias.playpad.pad.viewcontroller.AbstractPadViewController;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.project.ProjectSettings;
+import de.tobias.playpad.project.api.IPad;
 import de.tobias.playpad.project.page.PadIndex;
 import de.tobias.playpad.project.page.Page;
 import de.tobias.playpad.project.page.PageCoordinate;
@@ -39,7 +43,7 @@ import java.util.UUID;
  * @author tobias
  * @version 6.2.0
  */
-public class Pad {
+public class Pad implements IPad {
 
 	private UUID uuid;
 	private IntegerProperty positionProperty = new SimpleIntegerProperty();
@@ -62,6 +66,7 @@ public class Pad {
 	 */
 
 	// Global Listener (unabhängig von der UI), für Core Functions wie Play, Pause
+	private transient PadNameChangeListener padNameChangeListener;
 	private transient PadStatusControlListener padStatusControlListener;
 	private transient PadStatusNotFoundListener padStatusNotFoundListener;
 	private transient PadFadeContentListener padFadeContentListener;
@@ -73,6 +78,7 @@ public class Pad {
 	private transient PadTriggerStatusListener padTriggerStatusListener;
 	private transient PadTriggerDurationListener padTriggerDurationListener;
 	private transient PadTriggerContentListener padTriggerContentListener;
+	private transient PadTriggerPlaylistListener padTriggerPlaylistListener;
 	private transient boolean ignoreTrigger = false;
 
 	// Utils
@@ -93,10 +99,10 @@ public class Pad {
 		padListener = new PadUpdateListener(this);
 	}
 
-	public Pad(Project project, int index, Page page) {
+	public Pad(Project project, int position, Page page) {
 		this(project);
 
-		setPosition(index);
+		setPosition(position);
 		setPage(page);
 		setStatus(PadStatus.EMPTY);
 
@@ -117,6 +123,9 @@ public class Pad {
 
 	private void initPadListener() {
 		// Remove old listener from properties
+		if (padNameChangeListener != null && nameProperty != null) {
+			nameProperty.removeListener(padNameChangeListener);
+		}
 		if (padStatusControlListener != null && statusProperty != null) {
 			statusProperty.removeListener(padStatusControlListener);
 		}
@@ -127,6 +136,11 @@ public class Pad {
 			contentProperty.removeListener(padTriggerContentListener);
 			padTriggerContentListener.changed(contentProperty, getContent(), null);
 		}
+		if (padTriggerPlaylistListener != null && contentProperty != null) {
+			if (getContent() instanceof Playlistable) {
+				((Playlistable) getContent()).removePlaylistListener(padTriggerPlaylistListener);
+			}
+		}
 
 		if (padFadeDurationListener != null && contentProperty != null) {
 			contentProperty.removeListener(padFadeContentListener);
@@ -134,18 +148,18 @@ public class Pad {
 		}
 
 		// init new listener for properties
+		padNameChangeListener = new PadNameChangeListener(this);
+		nameProperty.addListener(padNameChangeListener);
 		padStatusControlListener = new PadStatusControlListener(this);
 		statusProperty.addListener(padStatusControlListener);
 
 		// Fade
-
 		padFadeDurationListener = new PadFadeDurationListener(this);
 		padFadeContentListener = new PadFadeContentListener(this);
 		contentProperty.addListener(padFadeContentListener);
 		padFadeContentListener.changed(contentProperty, null, getContent());
 
 		// Not found status count
-
 		padStatusNotFoundListener = new PadStatusNotFoundListener(project);
 		statusProperty.addListener(padStatusNotFoundListener);
 
@@ -160,6 +174,8 @@ public class Pad {
 		contentProperty.addListener(padTriggerContentListener);
 		padTriggerContentListener.changed(contentProperty, null, getContent());
 
+		padTriggerPlaylistListener = new PadTriggerPlaylistListener();
+
 		// Pad Listener
 		if (mediaPathUpdateListener != null) {
 			mediaPaths.removeListener(mediaPathUpdateListener);
@@ -186,6 +202,7 @@ public class Pad {
 	 *
 	 * @return uuid
 	 */
+	@Override
 	public UUID getUuid() {
 		return uuid;
 	}
@@ -222,6 +239,7 @@ public class Pad {
 	 *
 	 * @return position
 	 */
+	@Override
 	public int getPositionReadable() {
 		return positionProperty.get() + 1;
 	}
@@ -231,6 +249,7 @@ public class Pad {
 	 *
 	 * @return position
 	 */
+	@Override
 	public int getPosition() {
 		return positionProperty.get();
 	}
@@ -275,6 +294,7 @@ public class Pad {
 	 *
 	 * @return name
 	 */
+	@Override
 	public String getName() {
 		return nameProperty.get();
 	}
@@ -397,6 +417,7 @@ public class Pad {
 	 *
 	 * @return status
 	 */
+	@Override
 	public PadStatus getStatus() {
 		return statusProperty.get();
 	}
@@ -604,6 +625,10 @@ public class Pad {
 		return padTriggerDurationListener;
 	}
 
+	public PadTriggerPlaylistListener getPadTriggerPlaylistListener() {
+		return padTriggerPlaylistListener;
+	}
+
 	public PadFadeDurationListener getPadFadeDurationListener() {
 		return padFadeDurationListener;
 	}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettings.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettings.java
index a935edd4808ae2d9f1c92c99286591e429334cd8..3b3d449b7838ec4e83c94f1b6a665f01301f2578 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettings.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettings.java
@@ -30,12 +30,12 @@ public class PadSettings {
 	private DoubleProperty volumeProperty = new SimpleDoubleProperty(1.0);
 	private DoubleProperty speedProperty = new SimpleDoubleProperty(1.0);
 	private BooleanProperty loopProperty = new SimpleBooleanProperty(false);
+	private BooleanProperty playOverlayProperty = new SimpleBooleanProperty(false);
 	private ObjectProperty<TimeMode> timeModeProperty = new SimpleObjectProperty<>();
 	private ObjectProperty<FadeSettings> fadeProperty = new SimpleObjectProperty<>();
 	private ObjectProperty<Duration> warningProperty = new SimpleObjectProperty<>();
 	private ObjectProperty<Duration> cueInProperty = new SimpleObjectProperty<>();
 
-	private BooleanProperty customDesignProperty = new SimpleBooleanProperty(false);
 	private ModernCartDesign design;
 
 	private Map<TriggerPoint, Trigger> triggers = new EnumMap<>(TriggerPoint.class);
@@ -101,6 +101,18 @@ public class PadSettings {
 		return loopProperty;
 	}
 
+	public boolean isPlayOverlay() {
+		return playOverlayProperty.get();
+	}
+
+	public void setPlayOverlay(boolean playOverlay) {
+		playOverlayProperty.set(playOverlay);
+	}
+
+	public BooleanProperty playOverlayProperty() {
+		return playOverlayProperty;
+	}
+
 	public boolean isCustomTimeMode() {
 		return timeModeProperty.isNotNull().get();
 	}
@@ -187,21 +199,9 @@ public class PadSettings {
 		return cueInProperty.get();
 	}
 
-	public boolean isCustomDesign() {
-		return customDesignProperty.get();
-	}
-
-	public void setCustomDesign(boolean customLayout) {
-		this.customDesignProperty.set(customLayout);
-	}
-
-	public BooleanProperty customDesignProperty() {
-		return customDesignProperty;
-	}
-
 	public ModernCartDesign getDesign() {
 		if (design == null) {
-			ModernCartDesign newDesign = new ModernCartDesign(pad);
+			ModernCartDesign newDesign = new ModernCartDesign.ModernCartDesignBuilder(pad).build();
 
 			if (pad.getProject().getProjectReference().isSync()) {
 				CommandManager.execute(Commands.DESIGN_ADD, pad.getProject().getProjectReference(), newDesign);
@@ -255,6 +255,7 @@ public class PadSettings {
 
 		clone.volumeProperty = new SimpleDoubleProperty(getVolume());
 		clone.loopProperty = new SimpleBooleanProperty(isLoop());
+		clone.playOverlayProperty = new SimpleBooleanProperty(isPlayOverlay());
 
 		if (isCustomTimeMode())
 			clone.timeModeProperty = new SimpleObjectProperty<>(getTimeMode());
@@ -271,10 +272,7 @@ public class PadSettings {
 		else
 			clone.warningProperty = new SimpleObjectProperty<>();
 
-		clone.customDesignProperty = new SimpleBooleanProperty(isCustomDesign());
-		if (design != null) {
-			clone.design = design.copy(pad);
-		}
+		clone.design = design.copy(pad);
 
 		clone.triggers = new EnumMap<>(TriggerPoint.class);
 		triggers.forEach((key, value) -> clone.triggers.put(key, value.copy()));
@@ -307,10 +305,10 @@ public class PadSettings {
 	//// Computed
 
 	public ModernColor getBackgroundColor() {
-		if (isCustomDesign()) {
+		if (design.isEnableCustomBackgroundColor()) {
 			return design.getBackgroundColor();
-		} else {
-			return Profile.currentProfile().getProfileSettings().getDesign().getBackgroundColor();
 		}
+
+		return Profile.currentProfile().getProfileSettings().getDesign().getBackgroundColor();
 	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettingsSerializer.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettingsSerializer.java
index 82e70e61fcdcf7c06ccc037ad6296898528be82b..adf5c3e4befdd7ec2e4d3b56ad08c9defd4fa611 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettingsSerializer.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/PadSettingsSerializer.java
@@ -21,13 +21,13 @@ public class PadSettingsSerializer {
 
 	private static final String VOLUME_ELEMENT = "Volume";
 	private static final String LOOP_ELEMENT = "Loop";
+	private static final String PLAY_OVERLAY_ELEMENT = "PlayOverlay";
 	private static final String TIME_MODE_ELEMENT = "TimeMode";
 	private static final String FADE_ELEMENT = "Fade";
 	private static final String WARNING_ELEMENT = "Warning";
 	private static final String CUE_IN_ELEMENT = "CueIn";
 
 	private static final String DESIGN_ELEMENT = "Design";
-	private static final String CUSTOM_DESIGN_ELEMENT = "custom";
 
 	private static final String CUSTOM_SETTINGS_ITEM_ELEMENT = "Item";
 	private static final String CUSTOM_SETTINGS_TYPE_ATTR = "key";
@@ -48,6 +48,8 @@ public class PadSettingsSerializer {
 			padSettings.setVolume(Double.parseDouble(settingsElement.element(VOLUME_ELEMENT).getStringValue()));
 		if (settingsElement.element(LOOP_ELEMENT) != null)
 			padSettings.setLoop(Boolean.parseBoolean(settingsElement.element(LOOP_ELEMENT).getStringValue()));
+		if (settingsElement.element(PLAY_OVERLAY_ELEMENT) != null)
+			padSettings.setPlayOverlay(Boolean.parseBoolean(settingsElement.element(PLAY_OVERLAY_ELEMENT).getStringValue()));
 		if (settingsElement.element(TIME_MODE_ELEMENT) != null)
 			padSettings.setTimeMode(TimeMode.valueOf(settingsElement.element(TIME_MODE_ELEMENT).getStringValue()));
 		if (settingsElement.element(FADE_ELEMENT) != null)
@@ -72,9 +74,6 @@ public class PadSettingsSerializer {
 		// Layout
 		Element designElement = settingsElement.element(DESIGN_ELEMENT);
 		if (designElement != null) {
-			if (designElement.attributeValue(CUSTOM_DESIGN_ELEMENT) != null) {
-				padSettings.setCustomDesign(Boolean.parseBoolean(designElement.attributeValue(CUSTOM_DESIGN_ELEMENT)));
-			}
 			ModernCartDesignSerializer serializer = new ModernCartDesignSerializer();
 			ModernCartDesign design = serializer.load(designElement, pad);
 			padSettings.setDesign(design);
@@ -112,6 +111,7 @@ public class PadSettingsSerializer {
 
 		settingsElement.addElement(VOLUME_ELEMENT).addText(String.valueOf(padSettings.getVolume()));
 		settingsElement.addElement(LOOP_ELEMENT).addText(String.valueOf(padSettings.isLoop()));
+		settingsElement.addElement(PLAY_OVERLAY_ELEMENT).addText(String.valueOf(padSettings.isPlayOverlay()));
 		if (padSettings.isCustomTimeMode())
 			settingsElement.addElement(TIME_MODE_ELEMENT).addText(String.valueOf(padSettings.getTimeMode()));
 		if (padSettings.isCustomWarning())
@@ -122,13 +122,10 @@ public class PadSettingsSerializer {
 		if (padSettings.getCueIn() != null)
 			settingsElement.addElement(CUE_IN_ELEMENT).addText(padSettings.getCueIn().toString());
 
-		// Layout
+		// Design
 		Element designElement = settingsElement.addElement(DESIGN_ELEMENT);
-		if (padSettings.isCustomDesign()) {
-			ModernCartDesignSerializer serializer = new ModernCartDesignSerializer();
-			serializer.save(designElement, padSettings.getDesign());
-		}
-		designElement.addAttribute(CUSTOM_DESIGN_ELEMENT, String.valueOf(padSettings.isCustomDesign()));
+		ModernCartDesignSerializer serializer = new ModernCartDesignSerializer();
+		serializer.save(designElement, padSettings.getDesign());
 
 		Element userInfoElement = settingsElement.addElement(CUSTOM_SETTINGS_ELEMENT);
 		for (String key : padSettings.getCustomSettings().keySet()) {
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java
index 6c6d943e9f88741e9891794bf5a68fa3d712a7df..fe9c1b0ea10fe260b6ac772068769d5838c2a5bf 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContent.java
@@ -34,7 +34,12 @@ public abstract class PadContent {
 
 	public abstract String getType();
 
-	public abstract void play();
+	/**
+	 * Start playing the media of the pad
+	 *
+	 * @param withFadeIn a fade that indicates whether the content gets fade in or not
+	 */
+	public abstract void play(boolean withFadeIn);
 
 	public abstract boolean stop();
 
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentPlaylistFactory.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentPlaylistFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..67d90ac300a467168926e76e95342e7730d3eb8a
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PadContentPlaylistFactory.java
@@ -0,0 +1,10 @@
+package de.tobias.playpad.pad.content;
+
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.mediapath.MediaPath;
+import javafx.scene.Node;
+
+public interface PadContentPlaylistFactory {
+
+	Node getCustomPlaylistItemView(Pad pad, MediaPath mediaPath);
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PlaylistListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PlaylistListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7a70f4d519d505cc4f64878aa412278dd469e39
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/PlaylistListener.java
@@ -0,0 +1,14 @@
+package de.tobias.playpad.pad.content;
+
+import de.tobias.playpad.pad.Pad;
+
+public interface PlaylistListener {
+
+	void onPlaylistStart(Pad pad);
+
+	void onPlaylistItemStart(Pad pad);
+
+	void onPlaylistItemEnd(Pad pad);
+
+	void onPlaylistEnd(Pad pad);
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java
index 1d15d9ad02184a18dc076b6cce7d739f8eba9552..b4b60ea6dc70853cfa768a90c9643c91bad8582c 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/content/Playlistable.java
@@ -4,6 +4,10 @@ import de.tobias.playpad.pad.mediapath.MediaPath;
 import javafx.beans.property.IntegerProperty;
 
 public interface Playlistable {
+
+	String SHUFFLE_SETTINGS_KEY = "shuffle";
+	String AUTO_NEXT_SETTINGS_KEY = "autoNext";
+
 	int getCurrentPlayingMediaIndex();
 
 	IntegerProperty currentPlayingMediaIndexProperty();
@@ -13,4 +17,8 @@ public interface Playlistable {
 	void next();
 
 	boolean isLoaded(MediaPath mediaPath);
+
+	void addPlaylistListener(PlaylistListener listener);
+
+	void removePlaylistListener(PlaylistListener listener);
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/AbstractFadeController.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/AbstractFadeController.java
index 3923a9e07e8f171ce6b4c2ea714d6ed273136694..21eb241dc28053bba7ffbb8787a8874b8f840a06 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/AbstractFadeController.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/AbstractFadeController.java
@@ -1,5 +1,6 @@
 package de.tobias.playpad.pad.fade;
 
+import de.thecodelabs.logger.Logger;
 import javafx.animation.Transition;
 import javafx.util.Duration;
 
@@ -42,6 +43,8 @@ public abstract class AbstractFadeController {
 	}
 
 	public void fade(double from, double to, Duration duration, Runnable onFinish) {
+		Logger.debug("Fading from {0} to {1} in {2}", from, to, duration);
+
 		if (currentFadeTransition != null) {
 			currentFadeTransition.stop();
 		}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java
index 214f832d81b61a8449170e5443a83a1fa32c57de..5aac293cf6f31ab2c25a852fd274a90a8c696fda 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/fade/listener/PadFadeDurationListener.java
@@ -30,7 +30,7 @@ public class PadFadeDurationListener implements ChangeListener<Duration> {
 			if (pad.getContent() instanceof Playlistable && ((Playlistable) pad.getContent()).hasNext()) {
 				return;
 			}
-			
+
 			if (pad.getContent() instanceof Durationable) {
 				final Durationable durationable = (Durationable) pad.getContent();
 
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadNameChangeListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadNameChangeListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..af19e4f1cdc06d24b3cefb77037f2d88af408b91
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadNameChangeListener.java
@@ -0,0 +1,25 @@
+package de.tobias.playpad.pad.listener;
+
+import de.thecodelabs.logger.Logger;
+import de.tobias.playpad.PlayPadPlugin;
+import de.tobias.playpad.pad.Pad;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+
+public class PadNameChangeListener implements ChangeListener<String> {
+
+	private final Pad pad;
+
+	public PadNameChangeListener(Pad pad) {
+		this.pad = pad;
+	}
+
+	@Override
+	public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+		try {
+			PlayPadPlugin.getInstance().getPadListener().forEach(listener -> listener.onNameChanged(pad, oldValue, newValue));
+		} catch (Exception e) {
+			Logger.error(e);
+		}
+	}
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadStatusControlListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadStatusControlListener.java
index 5067bad2fd287937bc336e5362a57ad854d7b609..5a98a8d2665046b20a47526e40c1d032b343e225 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadStatusControlListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/PadStatusControlListener.java
@@ -10,12 +10,14 @@ import de.tobias.playpad.pad.content.play.Seekable;
 import de.tobias.playpad.pad.fade.Fadeable;
 import de.tobias.playpad.profile.Profile;
 import de.tobias.playpad.profile.ProfileSettings;
+import de.tobias.playpad.settings.FadeSettings;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
+import javafx.util.Duration;
 
 public class PadStatusControlListener implements ChangeListener<PadStatus> {
 
-	private Pad pad;
+	private final Pad pad;
 
 	// Utils für Single Pad Playing
 	private static Pad currentPlayingPad; // Nur wenn ProfileSettings.isMultiplePlayer == false
@@ -30,7 +32,7 @@ public class PadStatusControlListener implements ChangeListener<PadStatus> {
 		ProfileSettings profileSettings = Profile.currentProfile().getProfileSettings();
 
 		try {
-			PlayPadPlugin.getInstance().getPadListener().forEach(listener -> listener.onStatusChange(pad, newValue));
+			PlayPadPlugin.getInstance().getPadListener().forEach(listener -> listener.onStatusChange(pad, oldValue, newValue));
 		} catch (Exception e) {
 			Logger.error(e);
 		}
@@ -41,7 +43,8 @@ public class PadStatusControlListener implements ChangeListener<PadStatus> {
 				pad.getProject().updateActivePlayerProperty();
 
 				// bei Single Pad Playing wird das alte Pad beendet.
-				if (!profileSettings.isMultiplePlayer()) {
+				// Und wenn das neu abzuspielende Pad nicht im Modus "PlayOverlay" ist.
+				if (!profileSettings.isMultiplePlayer() && !pad.getPadSettings().isPlayOverlay()) {
 					if (currentPlayingPad != null && currentPlayingPad != pad) {
 						if (currentPlayingPad.isPlay() || currentPlayingPad.isPaused()) {
 							currentPlayingPad.stop();
@@ -50,27 +53,36 @@ public class PadStatusControlListener implements ChangeListener<PadStatus> {
 					currentPlayingPad = pad;
 				}
 
+				boolean withFadeIn = false;
 				if (pad.getContent() instanceof Fadeable) {
-					if ((oldValue != PadStatus.PAUSE && padSettings.getFade().isFadeInStart()) || (oldValue == PadStatus.PAUSE && padSettings.getFade().isFadeInPause())) {
-						((Fadeable) pad.getContent()).fadeIn();
+					final FadeSettings fadeSettings = padSettings.getFade();
+					if ((oldValue != PadStatus.PAUSE && fadeSettings.isFadeInStart()) || (oldValue == PadStatus.PAUSE && fadeSettings.isFadeInPause())) {
+						if (fadeSettings.getFadeIn().greaterThanOrEqualTo(Duration.seconds(0.1))) { // A fade in less than 0.1s is not recognizable
+							final Fadeable fadeable = (Fadeable) pad.getContent();
+							fadeable.fadeIn();
+							withFadeIn = true;
+						}
 					}
 				}
-				pad.getContent().play();
+				pad.getContent().play(withFadeIn);
 			}
 		} else if (newValue == PadStatus.PAUSE) {
 			if (pad.getContent() instanceof Pauseable) {
-				if (pad.getContent() instanceof Fadeable && padSettings.getFade().isFadeOutPause()) {
-					((Fadeable) pad.getContent()).fadeOut(() -> ((Pauseable) pad.getContent()).pause());
-				} else {
-					((Pauseable) pad.getContent()).pause();
+				final FadeSettings fadeSettings = padSettings.getFade();
+				if (pad.getContent() instanceof Fadeable && fadeSettings.isFadeOutPause()) {
+					if (fadeSettings.getFadeOut().greaterThanOrEqualTo(Duration.seconds(0.1))) { // A fade in less than 0.1s is not recognizable
+						((Fadeable) pad.getContent()).fadeOut(() -> ((Pauseable) pad.getContent()).pause());
+						return;
+					}
 				}
+				((Pauseable) pad.getContent()).pause();
 			}
 		} else if (newValue == PadStatus.STOP) {
 			if (pad.getContent() != null) {
 				pad.getProject().updateActivePlayerProperty();
 
-				if (pad.getContent() instanceof Fadeable && !pad.isEof() && padSettings.getFade().isFadeOutStop()) { // Fade nur wenn Pad
-					// nicht am ende ist
+				// Fade nur wenn Pad nicht am Ende ist
+				if (pad.getContent() instanceof Fadeable && !pad.isEof() && padSettings.getFade().isFadeOutStop()) {
 					((Fadeable) pad.getContent()).fadeOut(() ->
 					{
 						pad.getContent().stop();
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerContentListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerContentListener.java
index 09d08d491d8846561e33ac6dcfcfdf1fdff718fa..ae9789f26c4e97b356fe92452cdd8b8f25f1dd32 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerContentListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerContentListener.java
@@ -2,6 +2,7 @@ package de.tobias.playpad.pad.listener.trigger;
 
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.content.PadContent;
+import de.tobias.playpad.pad.content.Playlistable;
 import de.tobias.playpad.pad.content.play.Durationable;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
@@ -9,7 +10,7 @@ import javafx.beans.value.ObservableValue;
 // Fügt Time Listener hinzu für neuen Content
 public class PadTriggerContentListener implements ChangeListener<PadContent> {
 
-	private Pad pad;
+	private final Pad pad;
 
 	public PadTriggerContentListener(Pad pad) {
 		this.pad = pad;
@@ -21,12 +22,20 @@ public class PadTriggerContentListener implements ChangeListener<PadContent> {
 			if (oldValue instanceof Durationable) {
 				((Durationable) oldValue).positionProperty().removeListener(pad.getPadTriggerDurationListener());
 			}
+
+			if (oldValue instanceof Playlistable) {
+				((Playlistable) oldValue).removePlaylistListener(pad.getPadTriggerPlaylistListener());
+			}
 		}
 
 		if (newValue != null) {
 			if (newValue instanceof Durationable) {
 				((Durationable) newValue).positionProperty().addListener(pad.getPadTriggerDurationListener());
 			}
+
+			if (newValue instanceof Playlistable) {
+				((Playlistable) newValue).addPlaylistListener(pad.getPadTriggerPlaylistListener());
+			}
 		}
 
 	}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java
index 834121676c93bc610e53b4ed255d8c06536991ae..62a45871da4a76927c530b31a39b525efdc50227 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerDurationListener.java
@@ -31,14 +31,24 @@ public class PadTriggerDurationListener implements ChangeListener<Duration> {
 				Profile currentProfile = Profile.currentProfile();
 				PadSettings padSettings = pad.getPadSettings();
 
-				// Execute Start Triggers
-				final Trigger startTrigger = padSettings.getTrigger(TriggerPoint.START);
-				startTrigger.handle(pad, currentTime, pad.getProject(), mainViewController, currentProfile);
-
-				// Execute End Trigger
-				final Duration leftTime = totalDuration.subtract(currentTime);
-				final Trigger endTrigger = padSettings.getTrigger(TriggerPoint.EOF);
-				endTrigger.handle(pad, leftTime, pad.getProject(), mainViewController, currentProfile);
+				if (TriggerPoint.START.isAvailable(pad)) {
+					// Execute Start Triggers
+					final Trigger startTrigger = padSettings.getTrigger(TriggerPoint.START);
+					startTrigger.handle(pad, currentTime, pad.getProject(), mainViewController, currentProfile);
+				}
+
+				if (TriggerPoint.PLAYLIST_ITEM_START.isAvailable(pad)) {
+					// Execute Start Triggers
+					final Trigger startTrigger = padSettings.getTrigger(TriggerPoint.PLAYLIST_ITEM_START);
+					startTrigger.handle(pad, currentTime, pad.getProject(), mainViewController, currentProfile);
+				}
+
+				if (TriggerPoint.EOF.isAvailable(pad)) {
+					// Execute End Trigger
+					final Duration leftTime = totalDuration.subtract(currentTime);
+					final Trigger endTrigger = padSettings.getTrigger(TriggerPoint.EOF);
+					endTrigger.handle(pad, leftTime, pad.getProject(), mainViewController, currentProfile);
+				}
 			}
 		}
 	}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerPlaylistListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerPlaylistListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..efba0d26413e8bbac6095e7a6bc6e3ed58b8b1ed
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerPlaylistListener.java
@@ -0,0 +1,47 @@
+package de.tobias.playpad.pad.listener.trigger;
+
+import de.tobias.playpad.PlayPadPlugin;
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.PadSettings;
+import de.tobias.playpad.pad.content.PlaylistListener;
+import de.tobias.playpad.profile.Profile;
+import de.tobias.playpad.tigger.Trigger;
+import de.tobias.playpad.tigger.TriggerPoint;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+import javafx.util.Duration;
+
+public class PadTriggerPlaylistListener implements PlaylistListener {
+	@Override
+	public void onPlaylistStart(Pad pad) {
+		handleTrigger(pad, TriggerPoint.PLAYLIST_START);
+	}
+
+	@Override
+	public void onPlaylistItemStart(Pad pad) {
+		handleTrigger(pad, TriggerPoint.PLAYLIST_ITEM_START);
+	}
+
+	@Override
+	public void onPlaylistItemEnd(Pad pad) {
+		handleTrigger(pad, TriggerPoint.PLAYLIST_ITEM_END);
+	}
+
+	@Override
+	public void onPlaylistEnd(Pad pad) {
+		handleTrigger(pad, TriggerPoint.PLAYLIST_END);
+	}
+
+	private void handleTrigger(Pad pad, TriggerPoint point) {
+		if (!pad.isIgnoreTrigger()) {
+			final PadSettings padSettings = pad.getPadSettings();
+			final Trigger trigger = padSettings.getTriggers().get(point);
+
+			final IMainViewController mainViewController = PlayPadPlugin.getInstance().getMainViewController();
+			final Profile currentProfile = Profile.currentProfile();
+
+			trigger.handle(pad, Duration.ZERO, pad.getProject(), mainViewController, currentProfile);
+		} else {
+			pad.setIgnoreTrigger(false);
+		}
+	}
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java
index 94459f05e67e68795cc885c73be904a798fced97..e5fe5653f76e08939ff7d06c4581d61b8097406c 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/pad/listener/trigger/PadTriggerStatusListener.java
@@ -21,17 +21,23 @@ public class PadTriggerStatusListener implements ChangeListener<PadStatus> {
 	}
 
 	@Override
-	public void changed(ObservableValue<? extends PadStatus> observable, PadStatus oldValue, PadStatus newValue) {
+	public void changed(ObservableValue<? extends PadStatus> observable, PadStatus oldState, PadStatus newValue) {
 		if (!pad.isIgnoreTrigger()) {
 			PadSettings padSettings = pad.getPadSettings();
 
 			// Execute Trigger
 			if (newValue == PadStatus.PLAY) {
-				executeTrigger(padSettings.getTriggers().get(TriggerPoint.START));
-			} else if (newValue == PadStatus.STOP) {
-				executeTrigger(padSettings.getTriggers().get(TriggerPoint.STOP));
-			} else if (oldValue == PadStatus.PLAY && newValue == PadStatus.READY && pad.isEof()) {
-				executeTrigger(padSettings.getTriggers().get(TriggerPoint.EOF_STATE));
+				if (TriggerPoint.START.isAvailable(pad)) {
+					executeTrigger(padSettings.getTriggers().get(TriggerPoint.START));
+				}
+			} else if (newValue == PadStatus.STOP && !pad.isEof()) {
+				if (TriggerPoint.STOP.isAvailable(pad)) {
+					executeTrigger(padSettings.getTriggers().get(TriggerPoint.STOP));
+				}
+			} else if (oldState == PadStatus.STOP && newValue == PadStatus.READY && pad.isEof()) {
+				if (TriggerPoint.EOF.isAvailable(pad)) {
+					executeTrigger(padSettings.getTriggers().get(TriggerPoint.EOF));
+				}
 			}
 		} else {
 			pad.setIgnoreTrigger(false);
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/Jni4NetBridgeInitializer.java b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/Jni4NetBridgeInitializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..fec2d6c9b019227def912bd336e34277ff8a5231
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/Jni4NetBridgeInitializer.java
@@ -0,0 +1,62 @@
+package de.tobias.playpad.plugin;
+
+import de.thecodelabs.utils.application.App;
+import de.thecodelabs.utils.application.ApplicationUtils;
+import de.thecodelabs.utils.application.container.PathType;
+import net.sf.jni4net.Bridge;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.Objects;
+
+public class Jni4NetBridgeInitializer {
+
+	private static final String[] RESOURCE_DLLS = {
+			"jni4net.n-0.8.8.0.dll",
+			"jni4net.n.w32.v40-0.8.8.0.dll",
+			"jni4net.n.w64.v40-0.8.8.0.dll",
+	};
+
+	private static boolean loaded;
+
+	public static void initialize() throws IOException {
+		if (loaded) {
+			return;
+		}
+
+		final App app = ApplicationUtils.getApplication();
+		Path resourceFolder = copyResources("j4n/", RESOURCE_DLLS, "j4n", Jni4NetBridgeInitializer.class.getClassLoader());
+
+		Bridge.setVerbose(app.isDebug());
+		Bridge.init(resourceFolder.toFile());
+		loaded = true;
+	}
+
+	public static void loadDll(ClassLoader classLoader, String classpathDirectory, String target, String proxyDll, String... resources) throws IOException {
+		Path resourceFolder = copyResources(classpathDirectory, resources, target, classLoader);
+		Bridge.LoadAndRegisterAssemblyFrom(resourceFolder.resolve(proxyDll).toFile(), classLoader);
+	}
+
+	private static Path copyResources(String classpathDirectory, String[] resources, String destination, ClassLoader classLoader) throws IOException {
+		final App app = ApplicationUtils.getApplication();
+		final Path resourceFolder = app.getPath(PathType.LIBRARY, destination);
+
+		if (Files.notExists(resourceFolder)) {
+			Files.createDirectories(resourceFolder);
+		}
+
+		Arrays.stream(resources).forEach(resource -> {
+			final Path dest = resourceFolder.resolve(resource);
+			try {
+				Files.copy(Objects.requireNonNull(classLoader.getResourceAsStream(classpathDirectory + resource)), dest, StandardCopyOption.REPLACE_EXISTING);
+			} catch (IOException e) {
+				throw new UncheckedIOException(e);
+			}
+		});
+		return resourceFolder;
+	}
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/MainWindowListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/MainWindowListener.java
index ed330ccf0418863959b60738f6010d162d03e286..c73a2d3a5541ee37aa951db33b3b1508fe31b0a3 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/MainWindowListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/MainWindowListener.java
@@ -4,6 +4,9 @@ import de.tobias.playpad.viewcontroller.main.IMainViewController;
 
 public interface MainWindowListener extends WindowListener<IMainViewController> {
 
+	default void onCurrentPageChanged(int newPage) {
+	}
+
 	default void loadMenuKeyBinding() {
 	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/ModernPluginManager.java b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/ModernPluginManager.java
index 40505787027f23d67c65d616d4ab9b889c0b347b..4184b24dfeda8fb42dd9fd9a21c097e66cce50a1 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/ModernPluginManager.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/ModernPluginManager.java
@@ -58,7 +58,7 @@ public class ModernPluginManager {
 	}
 
 	public void loadFile(Path path) {
-		if (path.endsWith("classes")) {
+		if (path.toString().endsWith("classes") || path.toString().endsWith("jar")) {
 			pluginManager.addPluginFile(path);
 		} else {
 			pluginManager.addPluginsOfDirectory(path);
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/PadListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/PadListener.java
index 5c9df06fdb5af598fe73958e56fed71265f4535b..40fda9dfc865a065c20d7340b0389557e9386593 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/plugin/PadListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/plugin/PadListener.java
@@ -13,13 +13,15 @@ import javafx.collections.ListChangeListener;
  */
 public interface PadListener {
 
+	void onNameChanged(Pad pad, String oldValue, String newValue);
+
 	/**
 	 * Call then ever the status of a pad will be changed
 	 *
 	 * @param pad      corresponding pad
 	 * @param newValue new status value
 	 */
-	void onStatusChange(Pad pad, PadStatus newValue);
+	void onStatusChange(Pad pad, PadStatus oldValue, PadStatus newValue);
 
 	default void onMediaPathChanged(Pad pad, ListChangeListener.Change<? extends MediaPath> value) {
 	}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java b/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java
index 7e50c4dc4866467c22a06cc7372e830b28cd36e7..e33b1d13a09be32fc49206571ce989c54a2b1f91 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/profile/Profile.java
@@ -146,6 +146,10 @@ public class Profile {
 
 			setCurrentProfile(profile);
 
+			// Update mapping with new actions
+			final Registry<ActionProvider> actions = PlayPadPlugin.getRegistries().getActions();
+			actions.getComponents().forEach(provider -> provider.createDefaultActions(currentMapping));
+
 			return profile;
 		}
 		throw new ProfileNotFoundException(ref);
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/profile/ProfileSettings.java b/PlayWallCore/src/main/java/de/tobias/playpad/profile/ProfileSettings.java
index 0b20a03f7e516161a6507e746031236a4c02c80e..96605a40bdbefca0e271aaeedbf833dcf672a30e 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/profile/ProfileSettings.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/profile/ProfileSettings.java
@@ -4,7 +4,6 @@ import de.thecodelabs.logger.Logger;
 import de.thecodelabs.storage.settings.UserDefaults;
 import de.thecodelabs.storage.settings.annotation.Key;
 import de.tobias.playpad.PlayPadPlugin;
-import de.tobias.playpad.action.feedback.LightMode;
 import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
 import de.tobias.playpad.design.modern.serializer.ModernGlobalDesignSerializer;
 import de.tobias.playpad.pad.TimeMode;
@@ -39,7 +38,7 @@ public class ProfileSettings {
 	@Key
 	private boolean midiActive = false;
 	@Key
-	private LightMode lightMode = LightMode.NORMAL;
+	private String midiColorMapping = "";
 
 	// Audio Output
 	@Key
@@ -87,8 +86,8 @@ public class ProfileSettings {
 		return midiDevice;
 	}
 
-	public LightMode getLightMode() {
-		return lightMode;
+	public String getMidiColorMapping() {
+		return midiColorMapping;
 	}
 
 	public String getMainLayoutType() {
@@ -140,8 +139,8 @@ public class ProfileSettings {
 		this.midiDevice = midiDevice;
 	}
 
-	public void setLightMode(LightMode lightMode) {
-		this.lightMode = lightMode;
+	public void setMidiColorMapping(String midiColorMapping) {
+		this.midiColorMapping = midiColorMapping;
 	}
 
 	public void setMainLayoutType(String mainLayoutType) {
@@ -204,7 +203,7 @@ public class ProfileSettings {
 	private static final String DESIGN_ELEMENT = "Design";
 	private static final String MIDI_ACTIVE_ELEMENT = "MidiActive";
 	private static final String MIDI_DEVICE_ELEMENT = "MidiDevice";
-	private static final String LIGHT_MODE = "LightMode";
+	private static final String MIDI_COLOR_MAPPING = "MidiColorMapping";
 
 	// File Handler
 	public static ProfileSettings load(Path path) throws DocumentException, IOException {
@@ -221,8 +220,8 @@ public class ProfileSettings {
 				profileSettings.setMidiDeviceName(root.element(MIDI_DEVICE_ELEMENT).getStringValue());
 			if (root.element(MIDI_ACTIVE_ELEMENT) != null)
 				profileSettings.setMidiActive(Boolean.parseBoolean(root.element(MIDI_ACTIVE_ELEMENT).getStringValue()));
-			if (root.element(LIGHT_MODE) != null)
-				profileSettings.setLightMode(LightMode.valueOf(root.element(LIGHT_MODE).getStringValue()));
+			if (root.element(MIDI_COLOR_MAPPING) != null)
+				profileSettings.setMidiColorMapping(root.elementText(MIDI_COLOR_MAPPING));
 
 			if (root.element(DESIGN_ELEMENT) != null) {
 				Element element = root.element(DESIGN_ELEMENT);
@@ -292,7 +291,7 @@ public class ProfileSettings {
 		if (midiDevice != null)
 			root.addElement(MIDI_DEVICE_ELEMENT).addText(midiDevice);
 		root.addElement(MIDI_ACTIVE_ELEMENT).addText(String.valueOf(midiActive));
-		root.addElement(LIGHT_MODE).addText(lightMode.name());
+		root.addElement(MIDI_COLOR_MAPPING).addText(midiColorMapping);
 
 		Element designElement = root.addElement(DESIGN_ELEMENT);
 		ModernGlobalDesignSerializer serializer = new ModernGlobalDesignSerializer();
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/Project.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/Project.java
index eedc4bcac8b6176a772fb4b133d1ac2a7d3682f6..dbf086b843d1215aea599007bd2036af8954f586 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/project/Project.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/Project.java
@@ -3,6 +3,7 @@ package de.tobias.playpad.project;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.PadStatus;
 import de.tobias.playpad.pad.mediapath.MediaPath;
+import de.tobias.playpad.project.api.IProject;
 import de.tobias.playpad.project.page.PadIndex;
 import de.tobias.playpad.project.page.Page;
 import de.tobias.playpad.project.ref.ProjectReference;
@@ -27,7 +28,7 @@ import java.util.stream.Collectors;
  * @author tobias
  * @since 6.0.0
  */
-public class Project {
+public class Project implements IProject {
 
 	/**
 	 * Project file extension.
@@ -73,6 +74,7 @@ public class Project {
 				.forEach(Pad::stop);
 	}
 
+	@Override
 	public ProjectSettings getSettings() {
 		return settings;
 	}
@@ -81,15 +83,18 @@ public class Project {
 		return projectReference;
 	}
 
+	@Override
 	public Pad getPad(int x, int y, int page) {
 		return getPage(page).getPad(x, y);
 	}
 
+	@Override
 	public Pad getPad(PadIndex index) {
 		Page page = pages.get(index.getPagePosition());
 		return page.getPad(index.getId());
 	}
 
+	@Override
 	public Pad getPad(UUID uuid) {
 		for (Page page : pages) {
 			for (Pad pad : page.getPads()) {
@@ -102,21 +107,19 @@ public class Project {
 	}
 
 	public void setPad(PadIndex index, Pad pad) {
-		if (pad == null) {
-			return;
-		}
-		// Remove Pad from old location
-		if (pad.getPage().getPosition() != index.getPagePosition()) {
-			Page oldPage = pad.getPage();
+		// Remove Pad from old location if page changed
+		if (pad != null && pad.getPage().getPosition() != index.getPagePosition()) {
+			final Page oldPage = pad.getPage();
 			if (oldPage.getPad(pad.getPosition()).equals(pad)) {
 				oldPage.setPad(index.getId(), null);
 			}
 		}
 
-		Page page = pages.get(index.getPagePosition());
+		final Page page = pages.get(index.getPagePosition());
 		page.setPad(index.getId(), pad);
 	}
 
+	@Override
 	public Collection<Pad> getPads() {
 		return getPads(p -> true);
 	}
@@ -135,6 +138,7 @@ public class Project {
 	}
 
 	// Pages
+	@Override
 	public Page getPage(int position) {
 		if (position >= ProjectSettings.MAX_PAGES) {
 			return null;
@@ -146,7 +150,7 @@ public class Project {
 		return pages.get(position);
 	}
 
-
+	@Override
 	public Page getPage(UUID uuid) {
 		for (Page page : pages) {
 			if (page.getId().equals(uuid)) {
@@ -156,6 +160,7 @@ public class Project {
 		return null;
 	}
 
+	@Override
 	public ObservableList<Page> getPages() {
 		// Create new page if all is empty (automatic)
 		if (pages.isEmpty()) {
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/ProjectSettings.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/ProjectSettings.java
index 0e4d089f1fa7f183a685ac292fc2fc363ca7cb2d..3752ac7216f80a8d5177323af98b2e88617285fb 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/project/ProjectSettings.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/ProjectSettings.java
@@ -1,20 +1,23 @@
 package de.tobias.playpad.project;
 
+import de.thecodelabs.storage.settings.UserDefaults;
 import de.thecodelabs.storage.settings.annotation.Key;
 import de.thecodelabs.utils.application.ApplicationUtils;
+import de.tobias.playpad.project.api.IProjectSettings;
 
 import java.nio.file.Path;
 
-public class ProjectSettings {
+public class ProjectSettings implements IProjectSettings {
 
-	public static final int MAX_PAGES;
+	public static int MAX_PAGES = 10;
 
 	static {
-		Object maxPages = ApplicationUtils.getApplication().getUserDefaults().getData("MAX_PAGES");
-		if (maxPages != null) {
-			MAX_PAGES = Integer.parseInt(maxPages.toString());
-		} else {
-			MAX_PAGES = 10;
+		final UserDefaults userDefaults = ApplicationUtils.getApplication().getUserDefaults();
+		if (userDefaults != null) {
+			Object maxPages = userDefaults.getData("MAX_PAGES");
+			if (maxPages != null) {
+				MAX_PAGES = Integer.parseInt(maxPages.toString());
+			}
 		}
 	}
 
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPad.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPad.java
new file mode 100644
index 0000000000000000000000000000000000000000..4aae5c73b676722ee7e530f1cca089e37edb7bf4
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPad.java
@@ -0,0 +1,17 @@
+package de.tobias.playpad.project.api;
+
+import de.tobias.playpad.pad.PadStatus;
+
+import java.util.UUID;
+
+public interface IPad {
+	UUID getUuid();
+
+	String getName();
+
+	PadStatus getStatus();
+
+	int getPosition();
+
+	int getPositionReadable();
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPage.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..d780c8457fa745c72d267e65693a49018d9135b8
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IPage.java
@@ -0,0 +1,22 @@
+package de.tobias.playpad.project.api;
+
+import java.util.Collection;
+import java.util.UUID;
+
+public interface IPage {
+	UUID getId();
+
+	int getPosition();
+
+	String getName();
+
+	IPad getPad(int position);
+
+	IPad getPad(int x, int y);
+
+	Collection<? extends IPad> getPads();
+
+	default int getPadPosition(int x, int y, IProjectSettings settings) {
+		return y * settings.getColumns() + x;
+	}
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProject.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProject.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e5716a7bd89485a25a6caff737cc558a24bbe2c
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProject.java
@@ -0,0 +1,25 @@
+package de.tobias.playpad.project.api;
+
+import de.tobias.playpad.project.page.PadIndex;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+public interface IProject {
+	IProjectSettings getSettings();
+
+	IPad getPad(int x, int y, int page);
+
+	IPad getPad(PadIndex index);
+
+	IPad getPad(UUID uuid);
+
+	Collection<? extends IPad> getPads();
+
+	IPage getPage(int index);
+
+	IPage getPage(UUID uuid);
+
+	List<? extends IPage> getPages();
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProjectSettings.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProjectSettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2970dcb0c4971f05b30c1b420ceabb2279038c5
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/api/IProjectSettings.java
@@ -0,0 +1,8 @@
+package de.tobias.playpad.project.api;
+
+public interface IProjectSettings {
+
+	int getColumns();
+
+	int getRows();
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/project/page/Page.java b/PlayWallCore/src/main/java/de/tobias/playpad/project/page/Page.java
index 24f207894be9dc118cf8c20439094e3ab46ce851..b791b8b094a824f034995a90b5df2d1505676bf5 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/project/page/Page.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/project/page/Page.java
@@ -3,6 +3,7 @@ package de.tobias.playpad.project.page;
 import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.project.Project;
 import de.tobias.playpad.project.ProjectSettings;
+import de.tobias.playpad.project.api.IPage;
 import de.tobias.playpad.server.sync.command.CommandManager;
 import de.tobias.playpad.server.sync.command.Commands;
 import de.tobias.playpad.server.sync.listener.upstream.PageUpdateListener;
@@ -16,7 +17,7 @@ import java.util.*;
  * @author tobias
  * @since 6.0.0
  */
-public class Page {
+public class Page implements IPage {
 
 	private UUID id;
 	private IntegerProperty positionProperty;
@@ -84,6 +85,7 @@ public class Page {
 	 *
 	 * @return id
 	 */
+	@Override
 	public UUID getId() {
 		return id;
 	}
@@ -93,6 +95,7 @@ public class Page {
 	 *
 	 * @return page position.
 	 */
+	@Override
 	public int getPosition() {
 		return positionProperty.get();
 	}
@@ -123,6 +126,7 @@ public class Page {
 	 *
 	 * @return name
 	 */
+	@Override
 	public String getName() {
 		return nameProperty.get();
 	}
@@ -157,28 +161,28 @@ public class Page {
 	/**
 	 * Get the pad at this index. It will create a new pad at this index if this index is empty. In case of a wrong index the method will throw an exception.
 	 *
-	 * @param id index
+	 * @param position index
 	 * @return pad
 	 * @throws IllegalArgumentException bad index
 	 */
-	public Pad getPad(int id) throws IllegalArgumentException {
+	public Pad getPad(int position) throws IllegalArgumentException {
 		ProjectSettings settings = projectReference.getSettings();
 		int maxId = settings.getRows() * settings.getColumns();
-		if (id < 0 || id > maxId) {
-			throw new IllegalArgumentException("Illegal index: index is " + id + " but it must in a range of 0 to " + maxId);
+		if (position < 0 || position > maxId) {
+			throw new IllegalArgumentException("Illegal index: index is " + position + " but it must in a range of 0 to " + maxId);
 		}
 
-		if (pads.stream().noneMatch(p -> p.getPosition() == id)) {
+		if (pads.stream().noneMatch(p -> p.getPosition() == position)) {
 			// Create new pad for positionProperty
-			Pad pad = new Pad(projectReference, id, this);
-			setPad(id, pad);
+			Pad pad = new Pad(projectReference, position, this);
+			setPad(position, pad);
 
 			if (projectReference.getProjectReference().isSync()) {
 				CommandManager.execute(Commands.PAD_ADD, projectReference.getProjectReference(), pad);
 				CommandManager.execute(Commands.PAD_SETTINGS_ADD, projectReference.getProjectReference(), pad.getPadSettings());
 			}
 		}
-		Optional<Pad> padOptional = pads.stream().filter(p -> p.getPosition() == id).findFirst();
+		Optional<Pad> padOptional = pads.stream().filter(p -> p.getPosition() == position).findFirst();
 		return padOptional.orElse(null);
 	}
 
@@ -189,20 +193,16 @@ public class Page {
 	 * @param y y position
 	 * @return pad
 	 */
+	@Override
 	public Pad getPad(int x, int y) {
 		ProjectSettings settings = projectReference.getSettings();
 		if (x < settings.getColumns() && y < settings.getRows()) {
-			int position = getPadPosition(x, y);
+			int position = getPadPosition(x, y, settings);
 			return getPad(position);
 		}
 		return null;
 	}
 
-	private int getPadPosition(int x, int y) {
-		ProjectSettings settings = projectReference.getSettings();
-		return y * settings.getColumns() + x;
-	}
-
 	/**
 	 * Set a pad to a new id. It overwrites the old pad. If the pad argument is null, it only removes the old pad.
 	 *
@@ -227,6 +227,7 @@ public class Page {
 	 *
 	 * @return pads
 	 */
+	@Override
 	public Collection<Pad> getPads() {
 		return Collections.unmodifiableCollection(pads);
 	}
@@ -275,7 +276,7 @@ public class Page {
 	 * @param lastPadIndex index of the last pad on the page
 	 */
 	private void insertPadAndShiftSuccessor(int x, int y, int lastPadIndex) {
-		int position = getPadPosition(x, y);
+		int position = getPadPosition(x, y, getProject().getSettings());
 		// Going backwards to avoid overwriting the next pad when updating the position of the current one.
 		for (int i = lastPadIndex - 1; i >= position; i--) {
 			getPad(i).setPosition(i + 1);
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignAddListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignAddListener.java
index d4c9f6a550021f0a992569f8e7ab2baf36c7d286..9a395331ee727d6e45dba8468b68fcc50770653f 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignAddListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignAddListener.java
@@ -31,9 +31,11 @@ public class DesignAddListener implements ServerListener {
 			if (project != null) {
 				Pad pad = project.getPad(padId);
 				if (pad != null) {
-					ModernCartDesign modernCartDesign = new ModernCartDesign(pad, uuid);
-					modernCartDesign.setBackgroundColor(backgroundColor);
-					modernCartDesign.setPlayColor(playColor);
+					ModernCartDesign modernCartDesign = new ModernCartDesign.ModernCartDesignBuilder(pad, uuid)
+							.withBackgroundColor(backgroundColor, true)
+							.withPlayColor(playColor, true)
+							.build();
+
 					pad.getPadSettings().setDesign(modernCartDesign);
 				}
 			}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignUpdateListener.java b/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignUpdateListener.java
index fd06d289d49901d970a3dcf8e980e9fe2822af26..5d84b107fb05ce49c99caedfa6a596cdd2c5ef1d 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignUpdateListener.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/server/sync/listener/downstream/pad/settings/design/DesignUpdateListener.java
@@ -41,13 +41,13 @@ public class DesignUpdateListener implements ServerListener {
 						if (field.equals(PropertyDef.DESIGN_BACKGROUND_COLOR)) {
 							Platform.runLater(() -> {
 								design.setBackgroundColor(color);
-								pad.getPadSettings().setCustomDesign(true);
+								design.setEnableCustomBackgroundColor(true);
 								mainViewController.loadUserCss();
 							});
 						} else if (field.equals(PropertyDef.DESIGN_PLAY_COLOR)) {
 							Platform.runLater(() -> {
 								design.setPlayColor(color);
-								pad.getPadSettings().setCustomDesign(true);
+								design.setEnableCustomPlayColor(true);
 								mainViewController.loadUserCss();
 							});
 						}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/settings/GlobalSettings.java b/PlayWallCore/src/main/java/de/tobias/playpad/settings/GlobalSettings.java
index 11820aa886ed7e11ebc5ebd4b7a551d81ccfe0fd..3ca3f35b88d2c739540ec6eda69820d343bb7e05 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/settings/GlobalSettings.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/settings/GlobalSettings.java
@@ -34,7 +34,7 @@ public class GlobalSettings {
 	private Path savePath;
 
 	// Key Binding
-	private KeyCollection keyCollection = new KeyCollection();
+	private final KeyCollection keyCollection = new KeyCollection();
 
 	// Update
 	private boolean autoUpdate = true;
@@ -57,6 +57,11 @@ public class GlobalSettings {
 	// Behaviour
 	private boolean openLastDocument = false;
 
+	// Autosave
+	private boolean enableAutosave = false;
+	private int autosaveIntervalInMinutes = 5;
+
+
 	public GlobalSettings() {
 	}
 
@@ -109,6 +114,14 @@ public class GlobalSettings {
 		return openLastDocument;
 	}
 
+	public boolean isEnableAutosave() {
+		return enableAutosave;
+	}
+
+	public int getAutosaveIntervalInMinutes() {
+		return autosaveIntervalInMinutes;
+	}
+
 	// Setter
 	public void setAutoUpdate(boolean autoUpdate) {
 		this.autoUpdate = autoUpdate;
@@ -154,6 +167,14 @@ public class GlobalSettings {
 		this.openLastDocument = openLastDocument;
 	}
 
+	public void setEnableAutosave(boolean enableAutosave) {
+		this.enableAutosave = enableAutosave;
+	}
+
+	public void setAutosaveIntervalInMinutes(int autosaveIntervalInMinutes) {
+		this.autosaveIntervalInMinutes = autosaveIntervalInMinutes;
+	}
+
 	// Save & Load Data
 
 	public static final String KEYS_ELEMENT = "Keys";
@@ -168,6 +189,8 @@ public class GlobalSettings {
 	private static final String CACHE_PATH_ELEMENT = "Cache-Path";
 	private static final String IGNORE_SAVE_DIALOG_ELEMENT = "IgnoreSaveDialog";
 	private static final String OPEN_LAST_DOCUMENT_ELEMENT = "OpenLastDocument";
+	private static final String ENABLE_AUTOSAVE = "EnableAutosave";
+	private static final String AUTOSAVE_INTERVAL = "AutosaveInterval";
 
 	/**
 	 * Lädt eine neue Instanz der Globalen Einstellungen.
@@ -232,6 +255,15 @@ public class GlobalSettings {
 				if (root.element(OPEN_LAST_DOCUMENT_ELEMENT) != null) {
 					settings.setOpenLastDocument(Boolean.parseBoolean(root.element(OPEN_LAST_DOCUMENT_ELEMENT).getStringValue()));
 				}
+
+				// Autosave
+				if (root.element(ENABLE_AUTOSAVE) != null) {
+					settings.setEnableAutosave(Boolean.parseBoolean(root.element(ENABLE_AUTOSAVE).getStringValue()));
+				}
+
+				if (root.element(AUTOSAVE_INTERVAL) != null) {
+					settings.setAutosaveIntervalInMinutes(Integer.parseInt(root.element(AUTOSAVE_INTERVAL).getStringValue()));
+				}
 			}
 			return settings;
 		} catch (DocumentException | IOException e) {
@@ -273,6 +305,10 @@ public class GlobalSettings {
 			// Behaviour
 			root.addElement(OPEN_LAST_DOCUMENT_ELEMENT).addText(String.valueOf(openLastDocument));
 
+			// Autosave
+			root.addElement(ENABLE_AUTOSAVE).addText(String.valueOf(enableAutosave));
+			root.addElement(AUTOSAVE_INTERVAL).addText(String.valueOf(autosaveIntervalInMinutes));
+
 			XMLWriter writer = new XMLWriter(Files.newOutputStream(savePath), OutputFormat.createPrettyPrint());
 			writer.write(document);
 			writer.close();
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/LocalPadTrigger.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/LocalPadTrigger.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab4668911416d81f86e9b3726a43f19a4b57745e
--- /dev/null
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/LocalPadTrigger.java
@@ -0,0 +1,8 @@
+package de.tobias.playpad.tigger;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface LocalPadTrigger {
+	List<UUID> getCarts();
+}
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java
index ba284a16cccbe20166ac9c86c41fbb9a822dc5e5..ee4a738906f97998cc149e0d907de82c167fd44e 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/Trigger.java
@@ -106,28 +106,37 @@ public class Trigger {
 
 	public void handle(Pad pad, Duration currentDuration, Project project, IMainViewController mainViewController, Profile currentProfile) {
 		for (TriggerItem item : items) {
-			if (triggerPoint == TriggerPoint.START) {
-				handleStartPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
-			} else if (triggerPoint == TriggerPoint.STOP) {
-				handleEndPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
-			} else if (triggerPoint == TriggerPoint.EOF) {
-				handleEndPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
-			} else if (triggerPoint == TriggerPoint.EOF_STATE) {
-				item.performAction(pad, project, mainViewController, currentProfile);
+			switch (triggerPoint) {
+				case START:
+					handleStartPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
+					break;
+				case STOP:
+					item.performAction(pad, project, mainViewController, currentProfile);
+					break;
+				case EOF:
+					if (item.getDurationFromPoint() == Duration.ZERO) {
+						if (pad.isEof() && item.getPerformedAt() == null) {
+							item.setPerformedAt(currentDuration);
+							item.performAction(pad, project, mainViewController, currentProfile);
+						} else {
+							item.setPerformedAt(null);
+						}
+					} else {
+						handleEndPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
+					}
+					break;
+				case PLAYLIST_START:
+				case PLAYLIST_ITEM_START:
+					handleStartPoint(pad, currentDuration, project, mainViewController, currentProfile, item);
+					break;
+				case PLAYLIST_ITEM_END:
+				case PLAYLIST_END:
+					item.performAction(pad, project, mainViewController, currentProfile);
+					break;
 			}
 		}
 	}
 
-	private void handleEndPoint(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile, TriggerItem item) {
-		// Wenn Trigger noch nicht gespielt wurde (null) und Zeit größer ist als gesetzte Zeit (oder 0)
-		if (item.getPerformedAt() == null && (item.getDurationFromPoint().greaterThan(duration) || duration.equals(Duration.ZERO))) {
-			item.performAction(pad, project, mainViewController, currentProfile);
-			item.setPerformedAt(duration);
-		} else if (item.getDurationFromPoint().lessThan(duration)) {
-			item.setPerformedAt(null);
-		}
-	}
-
 	private void handleStartPoint(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile, TriggerItem item) {
 		if (pad.getStatus() == PadStatus.PLAY) {
 			// Mitten drin, wenn die Zeit die gespielt wurde größer ist als die gesetzte und noch der Trigger noch nicht ausgeführt
@@ -142,4 +151,14 @@ public class Trigger {
 			}
 		}
 	}
+
+	private void handleEndPoint(Pad pad, Duration duration, Project project, IMainViewController mainViewController, Profile currentProfile, TriggerItem item) {
+		// Wenn Trigger noch nicht gespielt wurde (null) und Zeit größer ist als gesetzte Zeit (oder 0)
+		if (item.getPerformedAt() == null && (item.getDurationFromPoint().greaterThan(duration) || duration.equals(Duration.ZERO))) {
+			item.performAction(pad, project, mainViewController, currentProfile);
+			item.setPerformedAt(duration);
+		} else if (item.getDurationFromPoint().lessThan(duration)) {
+			item.setPerformedAt(null);
+		}
+	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java
index 118dc56017638413688062a0bd742f59ffc05dec..3872372739901d826233366244c371c5f437451f 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerItemFactory.java
@@ -13,5 +13,4 @@ public abstract class TriggerItemFactory extends Component {
 	public abstract TriggerItem newInstance(Trigger trigger);
 
 	public abstract NVC getSettingsController(TriggerItem item, IMainViewController mainViewController);
-
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java
index 4f1390d9e6ebda63f92fe7647d00ae0fe759df2c..1f6a6a4b19e0a33faabb6a0314e85dfa33348c7f 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/tigger/TriggerPoint.java
@@ -1,22 +1,40 @@
 package de.tobias.playpad.tigger;
 
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.content.Playlistable;
+
+import java.util.function.Predicate;
+
 public enum TriggerPoint {
 
-	START(true),
-	STOP(false),
-	EOF(true),
-	EOF_STATE(false);
+	START(true, pad -> !(pad.getContent() instanceof Playlistable)),
+	STOP(false, pad -> !(pad.getContent() instanceof Playlistable)),
+	EOF(true,  pad -> !(pad.getContent() instanceof Playlistable)),
+	PLAYLIST_START(false,  pad -> pad.getContent() instanceof Playlistable),
+	PLAYLIST_ITEM_START(true,  pad -> pad.getContent() instanceof Playlistable),
+	PLAYLIST_ITEM_END(false,  pad -> pad.getContent() instanceof Playlistable),
+	PLAYLIST_END(false,  pad -> pad.getContent() instanceof Playlistable);
 
 	/**
 	 * Defines if a trigger can be run after, before a certain event.
 	 */
 	private final boolean timeAppendable;
+	private final Predicate<Pad> availablePredicate;
 
-	TriggerPoint(boolean timeAppendable) {
+	TriggerPoint(boolean timeAppendable, Predicate<Pad> availablePredicate) {
 		this.timeAppendable = timeAppendable;
+		this.availablePredicate = availablePredicate;
 	}
 
 	public boolean isTimeAppendable() {
 		return timeAppendable;
 	}
+
+	public Predicate<Pad> getAvailablePredicate() {
+		return availablePredicate;
+	}
+
+	public boolean isAvailable(Pad pad) {
+		return availablePredicate.test(pad);
+	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java
index 2e16c3cb79abe3471b9e6c0c185852f9efb87816..0e7774ca30c7c9d29426c304409fc31f1fd06380 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/view/main/ProjectPreviewView.java
@@ -1,9 +1,9 @@
 package de.tobias.playpad.view.main;
 
-import de.tobias.playpad.pad.Pad;
 import de.tobias.playpad.pad.PadStatus;
-import de.tobias.playpad.project.Project;
-import de.tobias.playpad.project.page.Page;
+import de.tobias.playpad.project.api.IPad;
+import de.tobias.playpad.project.api.IPage;
+import de.tobias.playpad.project.api.IProject;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
 import javafx.geometry.Insets;
@@ -18,10 +18,10 @@ import java.util.List;
 
 public class ProjectPreviewView extends Pagination {
 
-	private final Project project;
-	private final ObservableList<Pad> selected;
+	private final IProject project;
+	private final ObservableList<IPad> selected;
 
-	public ProjectPreviewView(Project project, List<Pad> preSelect, int initialPage) {
+	public ProjectPreviewView(IProject project, List<? extends IPad> preSelect, int initialPage) {
 		super(project.getPages().size());
 		this.project = project;
 		this.selected = FXCollections.observableArrayList(preSelect);
@@ -30,7 +30,7 @@ public class ProjectPreviewView extends Pagination {
 		setPageFactory(this::getPageNode);
 	}
 
-	private Node getPageNode(int index) {
+	private Node getPageNode(int pageIndex) {
 		GridPane gridPane = new GridPane();
 		gridPane.setHgap(7);
 		gridPane.setVgap(7);
@@ -38,10 +38,10 @@ public class ProjectPreviewView extends Pagination {
 
 		gridPane.setPadding(new Insets(0, 0, 7, 0));
 
-		final Page page = project.getPage(index);
+		final IPage page = project.getPage(pageIndex);
 		for (int x = 0; x < project.getSettings().getColumns(); x++) {
 			for (int y = 0; y < project.getSettings().getRows(); y++) {
-				final Pad pad = page.getPad(x, y);
+				final IPad pad = page.getPad(x, y);
 				ToggleButton toggleButton = getToggleButton(selected, pad);
 
 				gridPane.add(toggleButton, x, y);
@@ -50,7 +50,7 @@ public class ProjectPreviewView extends Pagination {
 		return gridPane;
 	}
 
-	private ToggleButton getToggleButton(List<Pad> preSelect, Pad pad) {
+	private ToggleButton getToggleButton(List<IPad> preSelect, IPad pad) {
 		ToggleButton toggleButton = new ToggleButton(String.valueOf(pad.getPositionReadable()));
 		if (pad.getStatus() != PadStatus.EMPTY) {
 			toggleButton.setTooltip(new Tooltip(pad.getName()));
@@ -72,11 +72,11 @@ public class ProjectPreviewView extends Pagination {
 		return toggleButton;
 	}
 
-	public List<Pad> getSelected() {
+	public List<? extends IPad> getSelected() {
 		return selected;
 	}
 
-	public ObservableList<Pad> selectedProperty() {
+	public ObservableList<? extends IPad> selectedProperty() {
 		return selected;
 	}
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/viewcontroller/main/IMainViewController.java b/PlayWallCore/src/main/java/de/tobias/playpad/viewcontroller/main/IMainViewController.java
index 74d3f11ea2299ef40c7faf255474d20ed2b8f0be..3d9941f75c903002295a3bd584b07b1cfc490816 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/viewcontroller/main/IMainViewController.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/viewcontroller/main/IMainViewController.java
@@ -157,4 +157,9 @@ public interface IMainViewController extends NotificationHandler, Alertable {
 	<T extends Event> void addListenerForPads(EventHandler<? super T> handler, EventType<T> eventType);
 
 	<T extends Event> void removeListenerForPads(EventHandler<? super T> handler, EventType<T> eventType);
+
+	/**
+	 * Führt einen Speichervorgang aus.
+	 */
+	void save();
 }
diff --git a/PlayWallCore/src/main/java/de/tobias/playpad/volume/VolumeManager.java b/PlayWallCore/src/main/java/de/tobias/playpad/volume/VolumeManager.java
index 816bc326958767c61209b3779497a0b2907ec821..9c6c9846bae46f7b8bc53fd0161e154166c801c7 100644
--- a/PlayWallCore/src/main/java/de/tobias/playpad/volume/VolumeManager.java
+++ b/PlayWallCore/src/main/java/de/tobias/playpad/volume/VolumeManager.java
@@ -17,7 +17,7 @@ public class VolumeManager {
 		return instance;
 	}
 
-	private List<VolumeFilter> filters;
+	private final List<VolumeFilter> filters;
 
 	private VolumeManager() {
 		this.filters = new ArrayList<>();
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n-0.8.8.0.dll b/PlayWallCore/src/main/resources/j4n/jni4net.n-0.8.8.0.dll
old mode 100755
new mode 100644
similarity index 100%
rename from PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n-0.8.8.0.dll
rename to PlayWallCore/src/main/resources/j4n/jni4net.n-0.8.8.0.dll
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n.w32.v40-0.8.8.0.dll b/PlayWallCore/src/main/resources/j4n/jni4net.n.w32.v40-0.8.8.0.dll
old mode 100755
new mode 100644
similarity index 100%
rename from PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n.w32.v40-0.8.8.0.dll
rename to PlayWallCore/src/main/resources/j4n/jni4net.n.w32.v40-0.8.8.0.dll
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n.w64.v40-0.8.8.0.dll b/PlayWallCore/src/main/resources/j4n/jni4net.n.w64.v40-0.8.8.0.dll
old mode 100755
new mode 100644
similarity index 100%
rename from PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.n.w64.v40-0.8.8.0.dll
rename to PlayWallCore/src/main/resources/j4n/jni4net.n.w64.v40-0.8.8.0.dll
diff --git a/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializerTest.java b/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9d45ba434dc424b0be1f71e1c0f24cfe7492244
--- /dev/null
+++ b/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernCartDesignSerializerTest.java
@@ -0,0 +1,78 @@
+package de.tobias.playpad.design.modern.serializer;
+
+import de.tobias.playpad.design.modern.ModernColor;
+import de.tobias.playpad.design.modern.model.ModernCartDesign;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.junit.Test;
+
+import java.io.InputStream;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ModernCartDesignSerializerTest {
+	private final ModernColor BACKGROUND_COLOR = ModernColor.BLUE1;
+	private final ModernColor PLAY_COLOR = ModernColor.GRAY5;
+	private final ModernColor CUE_IN_COLOR = ModernColor.PURPLE1;
+
+	private final UUID DESIGN_UUID = UUID.fromString("33977b97-60d6-49f1-897a-2f6f80de74e4");
+
+
+	@Test
+	public void testSave() {
+		// arrange
+		final ModernCartDesign design = new ModernCartDesign.ModernCartDesignBuilder(null, DESIGN_UUID)
+				.withBackgroundColor(BACKGROUND_COLOR, true)
+				.withPlayColor(PLAY_COLOR, true)
+				.withCueInColor(CUE_IN_COLOR, false)
+				.build();
+
+		final Document document = DocumentHelper.createDocument();
+		final Element rootElement = document.addElement("Design");
+
+		// act
+		final ModernCartDesignSerializer serializer = new ModernCartDesignSerializer();
+		serializer.save(rootElement, design);
+
+		// assert
+		assertThat(rootElement.attributeValue("id")).isEqualTo(DESIGN_UUID.toString());
+
+		assertThat(rootElement.element("EnableCustomBackgroundColor").getStringValue()).isEqualTo("true");
+		assertThat(rootElement.element("BackgroundColor").getStringValue()).isEqualTo(BACKGROUND_COLOR.name());
+
+		assertThat(rootElement.element("EnableCustomPlayColor").getStringValue()).isEqualTo("true");
+		assertThat(rootElement.element("PlayColor").getStringValue()).isEqualTo(PLAY_COLOR.name());
+
+		assertThat(rootElement.element("EnableCustomCueInColor").getStringValue()).isEqualTo("false");
+		assertThat(rootElement.element("CueInColor").getStringValue()).isEqualTo(CUE_IN_COLOR.name());
+	}
+
+	@Test
+	public void testLoad() throws DocumentException {
+		// arrange
+		final String filePath = "de/tobias/playpad/design/modern/serializer/modern_cart_design.xml";
+		final InputStream inputStream = ModernCartDesignSerializerTest.class.getClassLoader().getResourceAsStream(filePath);
+		final SAXReader reader = new SAXReader();
+		final Document document = reader.read(inputStream);
+
+		// act
+		final ModernCartDesignSerializer serializer = new ModernCartDesignSerializer();
+		final ModernCartDesign design = serializer.load(document.getRootElement(), null);
+
+		// assert
+		assertThat(design.getId()).isEqualTo(DESIGN_UUID);
+
+		assertThat(design.isEnableCustomBackgroundColor()).isTrue();
+		assertThat(design.getBackgroundColor()).isEqualTo(BACKGROUND_COLOR);
+
+		assertThat(design.isEnableCustomPlayColor()).isTrue();
+		assertThat(design.getPlayColor()).isEqualTo(PLAY_COLOR);
+
+		assertThat(design.isEnableCustomCueInColor()).isFalse();
+		assertThat(design.getCueInColor()).isEqualTo(CUE_IN_COLOR);
+	}
+}
diff --git a/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernGlobalDesignSerializerTest.java b/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernGlobalDesignSerializerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa6ed525ff513e47619b066e52f5772edbc27a32
--- /dev/null
+++ b/PlayWallCore/src/test/java/de/tobias/playpad/design/modern/serializer/ModernGlobalDesignSerializerTest.java
@@ -0,0 +1,73 @@
+package de.tobias.playpad.design.modern.serializer;
+
+import de.tobias.playpad.design.modern.ModernColor;
+import de.tobias.playpad.design.modern.model.ModernGlobalDesign;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.junit.Test;
+
+import java.io.InputStream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ModernGlobalDesignSerializerTest {
+	private final ModernColor BACKGROUND_COLOR = ModernColor.BLUE1;
+	private final ModernColor PLAY_COLOR = ModernColor.GRAY5;
+	private final ModernColor CUE_IN_COLOR = ModernColor.PURPLE1;
+	private final int INFO_FONT_SIZE = 12;
+	private final int TITLE_FONT_SIZE = 18;
+
+	@Test
+	public void testSave() {
+		// arrange
+		final ModernGlobalDesign design = new ModernGlobalDesign();
+		design.setFlatDesign(true);
+		design.setBackgroundColor(BACKGROUND_COLOR);
+		design.setPlayColor(PLAY_COLOR);
+		design.setCueInColor(CUE_IN_COLOR);
+		design.setInfoFontSize(INFO_FONT_SIZE);
+		design.setTitleFontSize(TITLE_FONT_SIZE);
+		design.setWarnAnimation(true);
+
+		final Document document = DocumentHelper.createDocument();
+		final Element rootElement = document.addElement("Design");
+
+		// act
+		final ModernGlobalDesignSerializer serializer = new ModernGlobalDesignSerializer();
+		serializer.save(rootElement, design);
+
+		// assert
+		assertThat(rootElement.element("FlatDesign").getStringValue()).isEqualTo("true");
+		assertThat(rootElement.element("BackgroundColor").getStringValue()).isEqualTo(BACKGROUND_COLOR.name());
+		assertThat(rootElement.element("PlayColor").getStringValue()).isEqualTo(PLAY_COLOR.name());
+		assertThat(rootElement.element("CueInColor").getStringValue()).isEqualTo(CUE_IN_COLOR.name());
+		assertThat(rootElement.element("InfoFontSize").getStringValue()).isEqualTo("12");
+		assertThat(rootElement.element("TitleFontSize").getStringValue()).isEqualTo("18");
+		assertThat(rootElement.element("Animation").element("Warn").getStringValue()).isEqualTo("true");
+	}
+
+	@Test
+	public void testLoad() throws DocumentException {
+		// arrange
+		final String filePath = "de/tobias/playpad/design/modern/serializer/modern_global_design.xml";
+		final InputStream inputStream = ModernGlobalDesignSerializerTest.class.getClassLoader().getResourceAsStream(filePath);
+		final SAXReader reader = new SAXReader();
+		final Document document = reader.read(inputStream);
+
+		// act
+		final ModernGlobalDesignSerializer serializer = new ModernGlobalDesignSerializer();
+		final ModernGlobalDesign design = serializer.load(document.getRootElement());
+
+		// assert
+		assertThat(design.isFlatDesign()).isTrue();
+		assertThat(design.getBackgroundColor()).isEqualTo(BACKGROUND_COLOR);
+		assertThat(design.getPlayColor()).isEqualTo(PLAY_COLOR);
+		assertThat(design.getCueInColor()).isEqualTo(CUE_IN_COLOR);
+		assertThat(design.getInfoFontSize()).isEqualTo(INFO_FONT_SIZE);
+		assertThat(design.getTitleFontSize()).isEqualTo(TITLE_FONT_SIZE);
+		assertThat(design.isWarnAnimation()).isTrue();
+	}
+}
diff --git a/PlayWallCore/src/test/java/de/tobias/playpad/pad/PadSettingsSerializerTest.java b/PlayWallCore/src/test/java/de/tobias/playpad/pad/PadSettingsSerializerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7f994051511b68bb1ee5063d1cccd8aae2953f1
--- /dev/null
+++ b/PlayWallCore/src/test/java/de/tobias/playpad/pad/PadSettingsSerializerTest.java
@@ -0,0 +1,52 @@
+package de.tobias.playpad.pad;
+
+import de.tobias.playpad.project.Project;
+import de.tobias.playpad.project.ref.ProjectReference;
+import de.tobias.playpad.settings.FadeSettings;
+import javafx.util.Duration;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+import org.junit.Test;
+
+import java.util.UUID;
+
+import static de.tobias.playpad.pad.PadSerializer.SETTINGS_ELEMENT;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PadSettingsSerializerTest {
+	@Test
+	public void testSave_General() {
+		// arrange
+		final Document document = DocumentHelper.createDocument();
+		final Element rootElement = document.addElement(SETTINGS_ELEMENT);
+
+		final Project project = new Project(new ProjectReference(UUID.randomUUID(), "TestProject", false));
+		final Pad pad = new Pad(project, 1, null);
+
+		final UUID padUUID = UUID.randomUUID();
+		final PadSettings padSettings = new PadSettings(pad, padUUID);
+		padSettings.setVolume(0.5);
+		padSettings.setLoop(true);
+		padSettings.setTimeMode(TimeMode.REST);
+		padSettings.setFade(new FadeSettings(Duration.seconds(1), Duration.seconds(2)));
+
+		final Duration expectedWarningDuration = Duration.seconds(3);
+		padSettings.setWarning(expectedWarningDuration);
+		final Duration expectedCueInDuration = Duration.seconds(4);
+		padSettings.setCueIn(expectedCueInDuration);
+
+		// act
+		final PadSettingsSerializer serializer = new PadSettingsSerializer();
+		serializer.saveElement(rootElement, padSettings);
+
+		// assert
+		assertThat(rootElement.attribute("id").getStringValue()).isEqualTo(padUUID.toString());
+		assertThat(rootElement.element("Volume").getStringValue()).isEqualTo("0.5");
+		assertThat(rootElement.element("Loop").getStringValue()).isEqualTo("true");
+		assertThat(rootElement.element("TimeMode").getStringValue()).isEqualTo(TimeMode.REST.toString());
+		assertThat(rootElement.element("Fade").getStringValue()).isNotNull();
+		assertThat(rootElement.element("Warning").getStringValue()).isEqualTo(expectedWarningDuration.toString());
+		assertThat(rootElement.element("CueIn").getStringValue()).isEqualTo(expectedCueInDuration.toString());
+	}
+}
diff --git a/PlayWallCore/src/test/resources/colors/ModernColor.json b/PlayWallCore/src/test/resources/colors/ModernColor.json
new file mode 100644
index 0000000000000000000000000000000000000000..eae7057a68a2decc1663d87c5c93690720c835ac
--- /dev/null
+++ b/PlayWallCore/src/test/resources/colors/ModernColor.json
@@ -0,0 +1,470 @@
+[
+    {
+        "name": "RED1",
+        "colors": {
+            "hi": "#ef9a9a",
+            "low": "#ef5350",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "RED2",
+        "colors": {
+            "hi": "#ef5350",
+            "low": "#e53935",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "RED3",
+        "colors": {
+            "hi": "#e53935",
+            "low": "#c62828",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "DARK_RED1",
+        "colors": {
+            "hi": "#D92349",
+            "low": "#AD2039",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "DARK_RED2",
+        "colors": {
+            "hi": "#C92349",
+            "low": "#8D2039",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "DARK_RED3",
+        "colors": {
+            "hi": "#A90329",
+            "low": "#6D0019",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PINK1",
+        "colors": {
+            "hi": "#f48fb1",
+            "low": "#ec407a",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PINK2",
+        "colors": {
+            "hi": "#ec407a",
+            "low": "#d81b60",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PINK3",
+        "colors": {
+            "hi": "#d81b60",
+            "low": "#ad1457",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PURPLE1",
+        "colors": {
+            "hi": "#ce93d8",
+            "low": "#ab47bc",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PURPLE2",
+        "colors": {
+            "hi": "#ab47bc",
+            "low": "#8e24aa",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "PURPLE3",
+        "colors": {
+            "hi": "#8e24aa",
+            "low": "#6a1b9a",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_BLUE1",
+        "colors": {
+            "hi": "#80deea",
+            "low": "#26c6da",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_BLUE2",
+        "colors": {
+            "hi": "#26c6da",
+            "low": "#00acc1",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_BLUE3",
+        "colors": {
+            "hi": "#00acc1",
+            "low": "#00838f",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "BLUE1",
+        "colors": {
+            "hi": "#90caf9",
+            "low": "#42a5f5",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "BLUE2",
+        "colors": {
+            "hi": "#42a5f5",
+            "low": "#1e88e5",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "BLUE3",
+        "colors": {
+            "hi": "#1e88e5",
+            "low": "#1565c0",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_GREEN1",
+        "colors": {
+            "hi": "#c5e1a5",
+            "low": "#9ccc65",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_GREEN2",
+        "colors": {
+            "hi": "#9ccc65",
+            "low": "#7cb342",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIGHT_GREEN3",
+        "colors": {
+            "hi": "#7cb342",
+            "low": "#558b2f",
+            "font": "#ffffff",
+            "button": "#ffffff",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIME1",
+        "colors": {
+            "hi": "#e6ee9c",
+            "low": "#d4e157",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIME2",
+        "colors": {
+            "hi": "#d4e157",
+            "low": "#c0ca33",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "LIME3",
+        "colors": {
+            "hi": "#c0ca33",
+            "low": "#9e9d24",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "YELLOW1",
+        "colors": {
+            "hi": "#fff59d",
+            "low": "#ffee58",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#333333",
+                "track": "#FFFFFF"
+            }
+        }
+    },
+    {
+        "name": "YELLOW2",
+        "colors": {
+            "hi": "#ffee58",
+            "low": "#fdd835",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#333333",
+                "track": "#FFFFFF"
+            }
+        }
+    },
+    {
+        "name": "YELLOW3",
+        "colors": {
+            "hi": "#fdd835",
+            "low": "#f9a825",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "ORANGE1",
+        "colors": {
+            "hi": "#ffcc80",
+            "low": "#ffa726",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "ORANGE2",
+        "colors": {
+            "hi": "#ffa726",
+            "low": "#fb8c00",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "ORANGE3",
+        "colors": {
+            "hi": "#fb8c00",
+            "low": "#ef6c00",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "GRAY1",
+        "colors": {
+            "hi": "#eeeeee",
+            "low": "#cccccc",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#333333",
+                "track": "#FFFFFF"
+            }
+        }
+    },
+    {
+        "name": "GRAY2",
+        "colors": {
+            "hi": "#cccccc",
+            "low": "#aaaaaa",
+            "font": "#000000",
+            "button": "#000000",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "GRAY3",
+        "colors": {
+            "hi": "#aaaaaa",
+            "low": "#888888",
+            "font": "#FFFFFF",
+            "button": "#FFFFFF",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "GRAY4",
+        "colors": {
+            "hi": "#888888",
+            "low": "#666666",
+            "font": "#FFFFFF",
+            "button": "#FFFFFF",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "GRAY5",
+        "colors": {
+            "hi": "#666666",
+            "low": "#444444",
+            "font": "#FFFFFF",
+            "button": "#FFFFFF",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    },
+    {
+        "name": "GRAY6",
+        "colors": {
+            "hi": "#444444",
+            "low": "#222222",
+            "font": "#FFFFFF",
+            "button": "#FFFFFF",
+            "playbar": {
+                "background": "#ffffff",
+                "track": "#333333"
+            }
+        }
+    }
+]
\ No newline at end of file
diff --git a/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_cart_design.xml b/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_cart_design.xml
new file mode 100644
index 0000000000000000000000000000000000000000..88091f5a057ef3b5ee3c5d64cf4ff19209c5a755
--- /dev/null
+++ b/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_cart_design.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Design id="33977b97-60d6-49f1-897a-2f6f80de74e4">
+    <EnableCustomBackgroundColor>true</EnableCustomBackgroundColor>
+    <BackgroundColor>BLUE1</BackgroundColor>
+    <EnableCustomPlayColor>true</EnableCustomPlayColor>
+    <PlayColor>GRAY5</PlayColor>
+    <EnableCustomCueInColor>false</EnableCustomCueInColor>
+    <CueInColor>PURPLE1</CueInColor>
+</Design>
\ No newline at end of file
diff --git a/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_global_design.xml b/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_global_design.xml
new file mode 100644
index 0000000000000000000000000000000000000000..da9d358e8053997445ffe9e9101893b38db1deed
--- /dev/null
+++ b/PlayWallCore/src/test/resources/de/tobias/playpad/design/modern/serializer/modern_global_design.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Design>
+    <BackgroundColor>BLUE1</BackgroundColor>
+    <PlayColor>GRAY5</PlayColor>
+    <CueInColor>PURPLE1</CueInColor>
+    <Animation>
+        <Warn>true</Warn>
+    </Animation>
+    <InfoFontSize>12</InfoFontSize>
+    <TitleFontSize>18</TitleFontSize>
+    <FlatDesign>true</FlatDesign>
+</Design>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginAwake/pom.xml b/PlayWallPlugins/PlayWallPluginAwake/pom.xml
index ab7412fa66fc468da72597b261d0699a109f60ce..4adbb1fe997eb1c95e4d63c22fd5a576443fbd3f 100644
--- a/PlayWallPlugins/PlayWallPluginAwake/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginAwake/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallPluginAwake</artifactId>
diff --git a/PlayWallPlugins/PlayWallPluginAwake/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginAwake/src/main/resources/plugin.yml
index 31d4e6bacc40bcae08702a0d37d4afc9233232f3..1bea69c3d21b2f3bb8d9d3c0fe37a2b1a4289bff 100644
--- a/PlayWallPlugins/PlayWallPluginAwake/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginAwake/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "AwakePlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 5
+build: 6
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/.gitignore b/PlayWallPlugins/PlayWallPluginContentPlayer/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8fe525802e3af85e9d1722fafbecab2998fac0f7
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/.gitignore
@@ -0,0 +1,3 @@
+j4n/clr
+j4n/jvm
+j4n/target
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.dll
new file mode 100644
index 0000000000000000000000000000000000000000..508b47da72d52b2beab41f465d920b91a9a7cd8a
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.dll
new file mode 100644
index 0000000000000000000000000000000000000000..37fae817262e9a355caaf268651bbe6d9b4dc678
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.jar b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.jar
new file mode 100644
index 0000000000000000000000000000000000000000..e6480f9d5319f63faa34e30e89b05d3829c560a9
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.j4n.jar differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.proxygen.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.proxygen.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f219b8cfb4ce278e6786df1883adf0039eee7456
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/NativeContentPlayerWindows.proxygen.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<jni4net-proxygen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jni4net.sf.net/0.8.8.0/toolConfig.xsd">
+  <TargetDirJvm>.\jvm</TargetDirJvm>
+  <TargetDirClr>.\clr</TargetDirClr>
+  <AssemblyReference Assembly="NativeContentPlayerWindows.dll" Generate="true" />
+</jni4net-proxygen>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/PVS.MediaPlayer.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/PVS.MediaPlayer.dll
new file mode 100644
index 0000000000000000000000000000000000000000..617ec46d3a2502d635507d9053bb57c3a858bb04
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/PVS.MediaPlayer.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/build.cmd b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/build.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..2db6957c49be54c3b41e80d40f15d8a33774385d
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/build.cmd
@@ -0,0 +1,21 @@
+@echo off
+if not exist target mkdir target
+if not exist target\classes mkdir target\classes
+
+
+echo compile classes
+javac -nowarn -d target\classes -sourcepath jvm -cp "c:\users\tobias\ideaprojects\playwalldesktop\playwallplugins\playwallplugincontentplayer\j4n\jni4net.j-0.8.8.0.jar"; "jvm\nativecontentplayerwindows\ContentPlayerStopListener.java" "jvm\nativecontentplayerwindows\ContentPlayerStopListener_.java" "jvm\nativecontentplayerwindows\ContentPlayerPositionListener.java" "jvm\nativecontentplayerwindows\ContentPlayerPositionListener_.java" "jvm\nativecontentplayerwindows\Zone.java" "jvm\nativecontentplayerwindows\ContentPlayer.java" "jvm\nativecontentplayerwindows\ContentScreen.java" "jvm\nativecontentplayerwindows\ContentPlayerWindow.java" 
+IF %ERRORLEVEL% NEQ 0 goto end
+
+
+echo NativeContentPlayerWindows.j4n.jar 
+jar cvf NativeContentPlayerWindows.j4n.jar  -C target\classes "nativecontentplayerwindows\ContentPlayerStopListener.class"  -C target\classes "nativecontentplayerwindows\ContentPlayerStopListener_.class"  -C target\classes "nativecontentplayerwindows\__ContentPlayerStopListener.class"  -C target\classes "nativecontentplayerwindows\ContentPlayerPositionListener.class"  -C target\classes "nativecontentplayerwindows\ContentPlayerPositionListener_.class"  -C target\classes "nativecontentplayerwindows\__ContentPlayerPositionListener.class"  -C target\classes "nativecontentplayerwindows\Zone.class"  -C target\classes "nativecontentplayerwindows\ContentPlayer.class"  -C target\classes "nativecontentplayerwindows\ContentScreen.class"  -C target\classes "nativecontentplayerwindows\ContentPlayerWindow.class"  > nul 
+IF %ERRORLEVEL% NEQ 0 goto end
+
+
+echo NativeContentPlayerWindows.j4n.dll 
+csc /nologo /warn:0 /t:library /out:NativeContentPlayerWindows.j4n.dll /recurse:clr\*.cs  /reference:"C:\Users\tobias\IdeaProjects\PlayWallDesktop\PlayWallPlugins\PlayWallPluginContentPlayer\j4n\NativeContentPlayerWindows.dll" /reference:"C:\Users\tobias\IdeaProjects\PlayWallDesktop\PlayWallPlugins\PlayWallPluginContentPlayer\j4n\jni4net.n-0.8.8.0.dll"
+IF %ERRORLEVEL% NEQ 0 goto end
+
+
+:end
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/generate.bat b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/generate.bat
new file mode 100644
index 0000000000000000000000000000000000000000..4c1670863c6366d9714187d5dcd97c0aa1324dc7
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/generate.bat
@@ -0,0 +1 @@
+proxygen.exe NativeContentPlayerWindows.dll -wd .
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.j-0.8.8.0.jar b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.j-0.8.8.0.jar
old mode 100755
new mode 100644
similarity index 100%
rename from PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/jni4net.j-0.8.8.0.jar
rename to PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.j-0.8.8.0.jar
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n-0.8.8.0.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n-0.8.8.0.dll
new file mode 100644
index 0000000000000000000000000000000000000000..2c7dc61711aea95d4a9d27f72ee9b540625ec0da
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n-0.8.8.0.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w32.v40-0.8.8.0.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w32.v40-0.8.8.0.dll
new file mode 100644
index 0000000000000000000000000000000000000000..e90e34defd278af539cd5d40a44f5d5f6b3b983d
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w32.v40-0.8.8.0.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w64.v40-0.8.8.0.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w64.v40-0.8.8.0.dll
new file mode 100644
index 0000000000000000000000000000000000000000..6ff98f1eca73982a4d293447484864290d2d2170
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/jni4net.n.w64.v40-0.8.8.0.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe
new file mode 100644
index 0000000000000000000000000000000000000000..816db4ef246603f6313670c111e4de0957905d56
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe.config b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe.config
new file mode 100644
index 0000000000000000000000000000000000000000..d1918bf7e2c67fdebccd63ed8b432d5b39e8b7e6
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/j4n/proxygen.exe.config
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
+    <supportedRuntime version="v2.0.50727"/>
+  </startup>
+</configuration>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
index c63bba0cd0555730de475e6b1aa9b82dab82b458..79ef01a0dd458ffb5393d6b4e46d04dd8f5528c5 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/pom.xml
@@ -3,15 +3,17 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>PlayWallPlugins</artifactId>
         <groupId>de.tobias.playpad</groupId>
-        <version>7.1.0</version>
+        <artifactId>PlayWallPlugins</artifactId>
+        <version>7.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>PlayWallPluginContentPlayer</artifactId>
 
     <properties>
+        <NativeContentPlayerWindows.j4n.version>1.0.0-SNAPSHOT</NativeContentPlayerWindows.j4n.version>
+
         <project.outputDirectory>../../build/${project.version}</project.outputDirectory>
         <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName>
     </properties>
@@ -20,9 +22,21 @@
         <dependency>
             <groupId>de.tobias.playpad</groupId>
             <artifactId>PlayWallCore</artifactId>
-            <version>${project.version}</version>
+            <version>${core.version}</version>
             <scope>provided</scope>
         </dependency>
+
+        <dependency>
+            <groupId>de.tobias.playpad</groupId>
+            <artifactId>NativeContentPlayerWindows.j4n</artifactId>
+            <version>${NativeContentPlayerWindows.j4n.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.bramp.ffmpeg</groupId>
+            <artifactId>ffmpeg</artifactId>
+            <version>0.6.2</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -127,5 +141,4 @@
             </snapshots>
         </repository>
     </repositories>
-
 </project>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
index 32da3b2a4dcc9e49a66cbac1a9bb23b5528c2a53..00d342ec918b9413b516d3d10e98a0277b4abadd 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/PadContent.xml
@@ -1,6 +1,6 @@
 <Actions>
     <Component id="content_player" name="plugin.content.Player.name" icon="MONITOR_MULTIPLE"
-               class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon"
-               size="30">de.tobias.playpad.plugin.content.pad.ContentPlayerPadContentFactory
+               class="de.thecodelabs.utils.ui.icon.MaterialDesignIcon" size="30">
+        de.tobias.playpad.plugin.content.pad.ContentPlayerPadContentFactory
     </Component>
 </Actions>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.dll
new file mode 100644
index 0000000000000000000000000000000000000000..508b47da72d52b2beab41f465d920b91a9a7cd8a
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.j4n.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.j4n.dll
new file mode 100644
index 0000000000000000000000000000000000000000..37fae817262e9a355caaf268651bbe6d9b4dc678
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/NativeContentPlayerWindows.j4n.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/PVS.MediaPlayer.dll b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/PVS.MediaPlayer.dll
new file mode 100644
index 0000000000000000000000000000000000000000..617ec46d3a2502d635507d9053bb57c3a858bb04
Binary files /dev/null and b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/dlls/PVS.MediaPlayer.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
index f349884fe1a09c06e16bd93793838ac6430afbb8..bebc781bdfaa2f289e6839ca78379be5e32377ca 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/lang/base_de.properties
@@ -1,6 +1,7 @@
 plugin.content.Player.name=Content Player
 
 plugin.content.player.settings=Content Player
+plugin.content.player.settings.screen=Display
 plugin.content.player.settings.name=Name:
 plugin.content.player.settings.x=X:
 plugin.content.player.settings.y=Y:
@@ -10,8 +11,15 @@ plugin.content.player.settings.add=Hinzuf\u00FCgen
 plugin.content.player.settings.remove=L\u00F6schen
 plugin.content.player.settings.default_name=Zone
 
+plugin.content.player.playlist.size=Gr\u00F6\u00DFe
+plugin.content.player.playlist.convert=Konvertieren (VStack)
+
 plugin.content.pad.settings.lastFrame.label=Letztes Frame halten
 plugin.content.pad.settings.lastFrame.checkbox=Aktiv
 plugin.content.pad.settings.zone=Zonen
 plugin.content.pad.settings.zone.addAll=Alle anw\u00E4hlen
 plugin.content.pad.settings.zone.removeAll=Alle abw\u00E4hlen
+plugin.content.player.settings.ffmpeg=ffmpeg
+plugin.content.player.settings.ffprobe=ffprobe
+plugin.content.player.settings.ffmpeg_download=Download
+plugin.content.player.settings.ffmpeg_link=https://www.gyan.dev/ffmpeg/builds/
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
index 70a18e12c0441bf59f78ee543ce885c15034c894..ab799a2ebc8b70a0738d245c287c2a2dce96e1cc 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "ContentPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 1
+build: 2
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPlayerSettingsTab.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPlayerSettingsTab.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..97426353d2bd801d5ac178c6b7315a9e1583a33c
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ContentPlayerSettingsTab.fxml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import de.thecodelabs.utils.ui.scene.input.NumberTextField?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+<VBox spacing="14.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
+   <children>
+      <HBox alignment="CENTER_LEFT" maxHeight="50.0" spacing="14.0">
+         <children>
+            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.ffmpeg" />
+            <TextField fx:id="ffmpegTextField" prefWidth="300.0" HBox.hgrow="ALWAYS" />
+            <Button fx:id="ffmpegButton" mnemonicParsing="false" onAction="#onFfmpegHandle" />
+         </children>
+      </HBox>
+      <HBox alignment="CENTER_LEFT" maxHeight="50.0" spacing="14.0">
+         <children>
+            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.ffprobe" />
+            <TextField fx:id="ffprobeTextField" prefWidth="300.0" HBox.hgrow="ALWAYS" />
+            <Button fx:id="ffprobeButton" mnemonicParsing="false" onAction="#onFfprobeHandle" />
+         </children>
+      </HBox>
+      <HBox alignment="CENTER_LEFT" spacing="14.0">
+         <children>
+            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.ffmpeg_download" />
+            <Hyperlink fx:id="ffmpegDownloadLink" onAction="#onFfmpegDownloadLink" />
+         </children>
+      </HBox>
+      <Separator prefWidth="200.0" />
+      <HBox alignment="CENTER_LEFT" maxHeight="50.0" spacing="14.0">
+         <children>
+            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.screen" />
+            <ComboBox fx:id="screenComboBox" prefWidth="200.0" />
+         </children>
+      </HBox>
+      <HBox>
+         <children>
+            <VBox>
+               <children>
+                    <ListView fx:id="listView" prefWidth="250.0" VBox.vgrow="ALWAYS" />
+                    <HBox prefHeight="30.0" prefWidth="200.0" spacing="14.0">
+                        <children>
+                            <Button fx:id="addButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onAddHandle" text="%plugin.content.player.settings.add" HBox.hgrow="ALWAYS" />
+                            <Button fx:id="removeButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onRemoveHandle" text="%plugin.content.player.settings.remove" HBox.hgrow="ALWAYS" />
+                        </children>
+                     <padding>
+                        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+                     </padding>
+                    </HBox>
+               </children>
+            </VBox>
+              <VBox prefHeight="400.0" prefWidth="401.0" spacing="14.0">
+                  <children>
+                      <HBox alignment="CENTER_LEFT" spacing="14.0">
+                          <children>
+                              <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.name" />
+                              <TextField fx:id="nameTextField" />
+                          </children>
+                      </HBox>
+                      <HBox alignment="CENTER_LEFT" spacing="14.0">
+                          <children>
+                              <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.x" />
+                              <NumberTextField fx:id="xTextField" />
+                          </children>
+                      </HBox>
+                      <HBox alignment="CENTER_LEFT" spacing="14.0">
+                          <children>
+                              <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.y" />
+                              <NumberTextField fx:id="yTextField" />
+                          </children>
+                      </HBox>
+                      <HBox alignment="CENTER_LEFT" spacing="14.0">
+                          <children>
+                              <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.width" />
+                              <NumberTextField fx:id="widthTextField" />
+                          </children>
+                      </HBox>
+                      <HBox alignment="CENTER_LEFT" spacing="14.0">
+                          <children>
+                              <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.height" />
+                              <NumberTextField fx:id="heightTextField" />
+                          </children>
+                      </HBox>
+                  </children>
+                  <padding>
+                      <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+                  </padding>
+              </VBox>
+         </children>
+      </HBox>
+   </children>
+   <padding>
+      <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
+   </padding>
+</VBox>
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml
deleted file mode 100644
index 3428c4ea773b6b4a8e3838f54f9373d77107c718..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/PlayerView.fxml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-
-<?import javafx.scene.layout.Pane?>
-<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" />
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml
deleted file mode 100644
index 64036902597eb22c98b1b9881eee56e6aad82a12..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/resources/view/ZoneSettings.fxml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import de.thecodelabs.utils.ui.scene.input.NumberTextField?>
-<?import javafx.geometry.Insets?>
-<?import javafx.scene.control.*?>
-<?import javafx.scene.layout.*?>
-<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
-    <children>
-      <VBox layoutY="345.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
-         <children>
-              <ListView fx:id="listView" prefWidth="250.0" VBox.vgrow="ALWAYS" />
-              <HBox prefHeight="30.0" prefWidth="200.0" spacing="14.0">
-                  <children>
-                      <Button fx:id="addButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onAddHandle" text="%plugin.content.player.settings.add" HBox.hgrow="ALWAYS" />
-                      <Button fx:id="removeButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#onRemoveHandle" text="%plugin.content.player.settings.remove" HBox.hgrow="ALWAYS" />
-                  </children>
-               <padding>
-                  <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
-               </padding>
-              </HBox>
-         </children>
-      </VBox>
-        <VBox layoutX="200.0" prefHeight="400.0" prefWidth="401.0" spacing="14.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="250.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
-            <children>
-                <HBox alignment="CENTER_LEFT" spacing="14.0">
-                    <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.name" />
-                        <TextField fx:id="nameTextField" />
-                    </children>
-                </HBox>
-                <HBox alignment="CENTER_LEFT" spacing="14.0">
-                    <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.x" />
-                        <NumberTextField fx:id="xTextField" />
-                    </children>
-                </HBox>
-                <HBox alignment="CENTER_LEFT" spacing="14.0">
-                    <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.y" />
-                        <NumberTextField fx:id="yTextField" />
-                    </children>
-                </HBox>
-                <HBox alignment="CENTER_LEFT" spacing="14.0">
-                    <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.width" />
-                        <NumberTextField fx:id="widthTextField" />
-                    </children>
-                </HBox>
-                <HBox alignment="CENTER_LEFT" spacing="14.0">
-                    <children>
-                        <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%plugin.content.player.settings.height" />
-                        <NumberTextField fx:id="heightTextField" />
-                    </children>
-                </HBox>
-            </children>
-            <padding>
-                <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
-            </padding>
-        </VBox>
-    </children>
-</AnchorPane>
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
index 2756703ed75e039767b9c5ba3e09324f46653b98..d9d0f33da1ab6acb6338a0a64433468b8a5df30f 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/ContentPluginMain.scala
@@ -4,11 +4,13 @@ import de.thecodelabs.plugins.PluginDescriptor
 import de.thecodelabs.storage.settings.{Storage, StorageTypes}
 import de.thecodelabs.utils.util.Localization
 import de.tobias.playpad.PlayPadPlugin
-import de.tobias.playpad.plugin.content.player.ContentPlayerViewController
-import de.tobias.playpad.plugin.content.settings.{ZoneConfiguration, ZoneSettingsViewController}
-import de.tobias.playpad.plugin.{Module, PlayPadPluginStub, SettingsListener}
+import de.tobias.playpad.plugin.content.player.ContentPlayerWindowController
+import de.tobias.playpad.plugin.content.settings.{ContentPlayerPluginConfiguration, ContentPlayerSettingsViewController}
+import de.tobias.playpad.plugin.content.util.FfmpegUtils
+import de.tobias.playpad.plugin.{Jni4NetBridgeInitializer, Module, PlayPadPluginStub, SettingsListener}
 import de.tobias.playpad.profile.{Profile, ProfileListener}
 import javafx.application.Platform
+import nativecontentplayerwindows.ContentPlayerWindow
 
 class ContentPluginMain extends PlayPadPluginStub with SettingsListener with ProfileListener {
 
@@ -17,25 +19,32 @@ class ContentPluginMain extends PlayPadPluginStub with SettingsListener with Pro
 	override def startup(descriptor: PluginDescriptor): Unit = {
 		module = new Module(descriptor.getName, descriptor.getArtifactId)
 
+		Jni4NetBridgeInitializer.initialize()
+		Jni4NetBridgeInitializer.loadDll(getClass.getClassLoader, "dlls/", "j4n", "NativeContentPlayerWindows.j4n.dll",
+			"NativeContentPlayerWindows.j4n.dll", "NativeContentPlayerWindows.dll", "PVS.MediaPlayer.dll")
+
+		ContentPluginMain.window = new ContentPlayerWindow()
+		ContentPluginMain.window.SetSize(1440, 80)
+
 		val localization = Localization.loadBundle("lang/base", getClass.getClassLoader)
 		Localization.addResourceBundle(localization)
 
 		PlayPadPlugin.getRegistries.getPadContents.loadComponentsFromFile("PadContent.xml", getClass.getClassLoader, module, localization)
-		PlayPadPlugin.getInstance().addAdditionalProfileSettingsTab(() => new ZoneSettingsViewController)
+		PlayPadPlugin.getInstance().addAdditionalProfileSettingsTab(() => new ContentPlayerSettingsViewController)
 
 		PlayPadPlugin.getInstance().addSettingsListener(this)
 		Profile.registerListener(this)
 	}
 
 	override def shutdown(): Unit = {
-		ContentPluginMain.playerViewController.getStageContainer.ifPresent(container => container.forceClose())
+		ContentPluginMain.playerViewController.window.Close()
 	}
 
 	override def getModule: Module = module
 
 	override def onLoad(profile: Profile): Unit = {
 		val path = profile.getRef.getCustomFilePath("Zones.json")
-		val zoneConfiguration = Storage.load(path, StorageTypes.JSON, classOf[ZoneConfiguration])
+		val zoneConfiguration = Storage.load(path, StorageTypes.JSON, classOf[ContentPlayerPluginConfiguration])
 		profile.addCustomSettings(ContentPluginMain.zoneConfigurationKey, zoneConfiguration)
 	}
 
@@ -48,13 +57,16 @@ class ContentPluginMain extends PlayPadPluginStub with SettingsListener with Pro
 	}
 
 	override def reloadSettings(oldProfile: Profile, currentProfile: Profile): Unit = {
-		val zoneConfiguration = currentProfile.getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration]
-		Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(zoneConfiguration))
+		FfmpegUtils.initialize()
+
+		val pluginConfiguration = currentProfile.getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ContentPlayerPluginConfiguration]
+		Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(pluginConfiguration))
 	}
 }
 
 object ContentPluginMain {
-	lazy val playerViewController: ContentPlayerViewController = new ContentPlayerViewController
+	lazy val playerViewController: ContentPlayerWindowController = new ContentPlayerWindowController
+	private var window: ContentPlayerWindow = _
 
 	val zoneConfigurationKey = "ZoneConfiguration"
 }
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala
index 3c298dcbfb2fcf167ef3d682cf03bb71c3d6c9da..54235bf261dc2846348f8d67e28f47579ea02e18 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerMediaContainer.scala
@@ -1,57 +1,80 @@
 package de.tobias.playpad.plugin.content.pad
 
+import de.tobias.playpad.PlayPadPlugin
 import de.tobias.playpad.pad.PadStatus
 import de.tobias.playpad.pad.mediapath.MediaPath
 import de.tobias.playpad.plugin.content.ContentPluginMain
 import de.tobias.playpad.plugin.content.util._
-import javafx.scene.media.MediaPlayer
+import javafx.beans.property.{ObjectProperty, ReadOnlyObjectProperty, SimpleObjectProperty}
 import javafx.util.Duration
 
-class ContentPlayerMediaContainer(val content: ContentPlayerPadContent, val path: MediaPath, val mediaPlayer: MediaPlayer) {
-	def play(): Unit = {
-		content._durationProperty.bind(mediaPlayer.totalDurationProperty())
-		content._positionProperty.bind(mediaPlayer.currentTimeProperty())
-		ContentPluginMain.playerViewController.showMediaPlayer(content.getPad.getPadIndex, mediaPlayer, content.getSelectedZones)
+import java.nio.file.{Files, Path}
 
-		mediaPlayer.seek(Duration.ZERO)
-		mediaPlayer.play()
+class ContentPlayerMediaContainer(val content: ContentPlayerPadContent, private[pad] val mediaPath: MediaPath, val totalDuration: Duration) {
+
+	private val _totalDurationProperty: ObjectProperty[Duration] = new SimpleObjectProperty[Duration]()
+
+	_totalDurationProperty.set(totalDuration)
+
+	def getPath: Path = {
+		val sourcePath = mediaPath.getPath.toAbsolutePath
+
+		val globalSettings = PlayPadPlugin.getInstance.getGlobalSettings
+		val convertPath = globalSettings.getCachePath.resolve(sourcePath.getFileName + ".mp4")
+
+		if (Files.exists(convertPath)) {
+			return convertPath
+		}
+
+		sourcePath
+	}
+
+	def getTotalDuration: Duration = _totalDurationProperty.get()
+
+	def totalDurationProperty: ReadOnlyObjectProperty[Duration] = _totalDurationProperty
+
+	def play(withFadeIn: Boolean): Unit = {
+		ContentPluginMain.playerViewController.play(this, withFadeIn)
 
 		content.getPad.setEof(false)
-		content.currentPlayingMediaIndexProperty().set(content.getMediaPlayers.indexOf(this))
+		content.currentPlayingMediaIndexProperty().set(content.getMediaContainers.indexOf(this))
 	}
 
-	def resume(): Unit = {
-		mediaPlayer.play()
+	def resume(withFadeIn: Boolean): Unit = {
+		ContentPluginMain.playerViewController.resume(this, withFadeIn)
 	}
 
 	def pause(): Unit = {
-		mediaPlayer.pause()
+		ContentPluginMain.playerViewController.pause(this)
 	}
 
 	def next(): Unit = {
-		stop()
-
-		val players = content.getMediaPlayers
-		val index = players.indexOf(this)
-		content.currentPlayingMediaIndexProperty().set(index)
+		val players = content.getMediaContainers
+		val currentIndex = players.indexOf(this)
+		content.currentPlayingMediaIndexProperty().set(currentIndex)
 
-		if (index + 1 < players.length) {
-			players(index + 1).play()
+		if (currentIndex + 1 < players.length) {
+			if (content.getPad.getStatus == PadStatus.PLAY) {
+				players(currentIndex + 1).play(false)
+			}
 		} else if (content.getPad.getPadSettings.isLoop) {
-			players.head.play()
+			if (content.getPad.getStatus == PadStatus.PLAY) {
+				players.head.play(false)
+			}
 		} else {
 			content.getPad.setStatus(PadStatus.STOP)
 		}
 	}
 
 	def stop(): Unit = {
-		mediaPlayer.stop()
-		ContentPluginMain.playerViewController.disconnectMediaPlayer(mediaPlayer, content.getSelectedZones)
+		ContentPluginMain.playerViewController.stop(this)
 
-		content._durationProperty.bind(content.totalDurationBinding())
-		content._positionProperty.unbind()
-		content._positionProperty.set(Duration.ZERO)
+		if (!content.getPad.getPadSettings.isLoop) {
+			content._durationProperty.bind(content.totalDurationBinding())
+			content._positionProperty.unbind()
+			content._positionProperty.set(Duration.ZERO)
+		}
 	}
 
-	override def toString: String = f"MediaPlayerContainer: $path"
+	override def toString: String = f"MediaPlayerContainer: $mediaPath"
 }
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala
index c1db519a17b8119362c64e7385ba26d91f2b9a40..1271fc9784635658409a013de478bb5d64b5f209 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContent.scala
@@ -1,27 +1,26 @@
 package de.tobias.playpad.plugin.content.pad
 
-import de.thecodelabs.logger.Logger
 import de.tobias.playpad.pad.content.play.{Durationable, Pauseable}
-import de.tobias.playpad.pad.content.{PadContent, Playlistable}
+import de.tobias.playpad.pad.content.{PadContent, PlaylistListener, Playlistable}
 import de.tobias.playpad.pad.fade.{Fadeable, LinearFadeController}
 import de.tobias.playpad.pad.mediapath.MediaPath
 import de.tobias.playpad.pad.{Pad, PadStatus}
 import de.tobias.playpad.plugin.content.ContentPluginMain
-import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration}
+import de.tobias.playpad.plugin.content.settings.{ContentPlayerPluginConfiguration, Zone}
 import de.tobias.playpad.plugin.content.util._
 import de.tobias.playpad.profile.Profile
-import de.tobias.playpad.volume.VolumeManager
+import de.tobias.playpad.tigger.{LocalPadTrigger, TriggerPoint}
 import javafx.application.Platform
 import javafx.beans.binding.{Bindings, ObjectBinding}
 import javafx.beans.property._
 import javafx.collections.{FXCollections, ObservableList}
-import javafx.scene.media.{Media, MediaPlayer}
 import javafx.util.Duration
+import nativecontentplayerwindows.ContentPlayer
 
 import java.nio.file.Files
 import java.util
-import java.util.Optional
 import java.util.stream.Collectors
+import java.util.{Collections, UUID}
 import scala.jdk.CollectionConverters._
 
 class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadContent(pad) with Pauseable with Durationable with Playlistable with Fadeable {
@@ -29,16 +28,19 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	private val mediaPlayers: ObservableList[ContentPlayerMediaContainer] = FXCollections.observableArrayList()
 	private val currentRunningIndexProperty: IntegerProperty = new SimpleIntegerProperty(-1)
 
-	private[pad] val _durationProperty = new SimpleObjectProperty[Duration]
-	private[pad] val _positionProperty = new SimpleObjectProperty[Duration]
+	private[content] val _durationProperty = new SimpleObjectProperty[Duration]
+	private[content] val _positionProperty = new SimpleObjectProperty[Duration]
+
+	private[content] val listeners: util.Set[PlaylistListener] = new util.HashSet[PlaylistListener]()
 
 	private var showingLastFrame: Boolean = false
 	private var isPause: Boolean = false
 
+	var stopMediaByOtherPlayer = false
+
 	private val fadeController = new LinearFadeController(value => {
 		if (getCurrentPlayingMediaIndex >= 0) {
-			ContentPluginMain.playerViewController
-				.setFadeValue(mediaPlayers(getCurrentPlayingMediaIndex).mediaPlayer, getSelectedZones, value)
+			ContentPluginMain.playerViewController.setFadeValue(getSelectedZones, value)
 		}
 	})
 
@@ -48,7 +50,7 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 
 	override def currentPlayingMediaIndexProperty(): IntegerProperty = currentRunningIndexProperty
 
-	def getMediaPlayers: ObservableList[ContentPlayerMediaContainer] = mediaPlayers
+	def getMediaContainers: ObservableList[ContentPlayerMediaContainer] = mediaPlayers
 
 	override def hasNext: Boolean = getCurrentPlayingMediaIndex + 1 < mediaPlayers.length
 
@@ -56,17 +58,24 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	Control Methods
 	 */
 
-	override def play(): Unit = {
+	override def play(withFadeIn: Boolean): Unit = {
 		if (isPause) {
-			mediaPlayers(getCurrentPlayingMediaIndex).resume()
+			mediaPlayers(getCurrentPlayingMediaIndex).resume(withFadeIn)
 		} else {
-			ContentPluginMain.playerViewController.addActivePadToList(getPad.getPadIndex, getSelectedZones)
-
+			if (isShuffle) {
+				Collections.shuffle(mediaPlayers)
+			} else {
+				reorderMedia()
+			}
 			getPad.setEof(false)
-			mediaPlayers.head.play()
+			mediaPlayers.head.play(withFadeIn)
+
+			listeners.forEach(listener => listener.onPlaylistStart(pad))
+			listeners.forEach(listener => listener.onPlaylistItemStart(pad))
 		}
 		showingLastFrame = false
 		isPause = false
+		stopMediaByOtherPlayer = false
 	}
 
 	override def pause(): Unit = {
@@ -75,40 +84,74 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	}
 
 	override def next(): Unit = {
-		mediaPlayers(getCurrentPlayingMediaIndex).next()
+		listeners.forEach(listener => listener.onPlaylistItemEnd(pad))
+		if (pad.getStatus == PadStatus.PLAY) {
+			mediaPlayers(getCurrentPlayingMediaIndex).next()
+			listeners.forEach(listener => listener.onPlaylistItemStart(pad))
+		}
 	}
 
 	override def stop(): Boolean = {
-		isPause = false
-		mediaPlayers(getCurrentPlayingMediaIndex).stop()
-		currentRunningIndexProperty.set(-1)
+		if (isPause) {
+			play(false)
+		}
+		if (getCurrentPlayingMediaIndex != -1) {
+			mediaPlayers(getCurrentPlayingMediaIndex).stop()
+
+			listeners.forEach(listener => listener.onPlaylistEnd(pad))
 
-		ContentPluginMain.playerViewController.removeActivePadFromList(getPad.getPadIndex, getSelectedZones)
+			if (showingLastFrame && !stopMediaByOtherPlayer) {
+				ContentPluginMain.playerViewController.clearHold(mediaPlayers(getCurrentPlayingMediaIndex))
+			}
+		}
+
+		currentRunningIndexProperty.set(-1)
 		true
 	}
 
 	def onEof(): Unit = {
-		if (isFadeActive) {
-			ContentPluginMain.playerViewController.removeActivePadFromList(getPad.getPadIndex, getSelectedZones)
-			return
-		}
-
-		if (shouldShowLastFrame() && !showingLastFrame // Only is settings is enabled and not already in last frame state
-			&& !pad.getPadSettings.isLoop // Only go to last frame state, is looping is disabled
-		) {
-			getPad.setStatus(PadStatus.PAUSE)
-			showingLastFrame = true
-			return
+		val hasLocalPadTrigger = getPad.getPadSettings.getTrigger(TriggerPoint.EOF)
+		  .getItems
+		  .stream()
+		  .filter(item => item.isInstanceOf[LocalPadTrigger])
+		  .map(item => item.asInstanceOf[LocalPadTrigger])
+		  .filter(item => hasPadTriggerInterferingZones(item))
+		  .count() > 0
+
+		val noFurtherItemsInPlaylist = getCurrentPlayingMediaIndex + 1 == mediaPlayers.length
+
+		// By default the last frame will be displayed. Only under certain conditions the last frame will be cleared
+		// 1. User settings set to "Clear last frame"
+		// 2. There is no loop
+		// 3. There is no playlist
+		if (!pad.getPadSettings.isLoop && noFurtherItemsInPlaylist) {
+			if (shouldShowLastFrame() || hasLocalPadTrigger) {
+				showingLastFrame = true
+				return
+			} else {
+				ContentPluginMain.playerViewController.clearHold(mediaPlayers(getCurrentPlayingMediaIndex))
+			}
 		}
 
 		showingLastFrame = false
-
-		if (getPad.isEof) {
-			mediaPlayers(getCurrentPlayingMediaIndex).next()
-			return
+		// Only automatically go to the next playlist item, if auto next is active or
+		// the item is the last one (next() go into stop state if no item is left)
+		if (isAutoNext || noFurtherItemsInPlaylist) {
+			next()
 		}
 	}
 
+	private def hasPadTriggerInterferingZones(item: LocalPadTrigger): Boolean = {
+		item.getCarts.stream().anyMatch(id => {
+			val content = pad.getProject.getPad(id).getContent
+			if (!content.isInstanceOf[ContentPlayerPadContent]) {
+				return false
+			}
+			val targetZones = content.asInstanceOf[ContentPlayerPadContent].getSelectedZones
+			return targetZones.exists(zone => getSelectedZones.contains(zone))
+		})
+	}
+
 	/*
 	Durationable
 	 */
@@ -122,19 +165,16 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	override def positionProperty(): ReadOnlyObjectProperty[Duration] = _positionProperty
 
 	def totalDurationBinding(): ObjectBinding[Duration] = {
-		Bindings.createObjectBinding(() => mediaPlayers.stream()
-			.map(player => player.mediaPlayer.getTotalDuration)
-			.filter(duration => duration != null)
-			.reduce(Duration.ZERO, (o1: Duration, o2: Duration) => o1.add(o2)),
-			mediaPlayers.stream().map(player => {
-				if (player.mediaPlayer != null) {
-					player.mediaPlayer.totalDurationProperty()
-				} else {
-					null
-				}
-			})
-				.filter(o => o != null)
-				.toArray(size => new Array[ReadOnlyObjectProperty[Duration]](size)): _*)
+		Bindings.createObjectBinding(() => {
+			val durations: util.List[Duration] = mediaPlayers.stream()
+			  .map(player => player.getTotalDuration)
+			  .filter(duration => duration != null)
+			  .collect(Collectors.toList())
+			val totalDuration: Duration = durations
+			  .stream()
+			  .reduce(Duration.ZERO, (o1: Duration, o2: Duration) => o1.add(o2))
+			totalDuration
+		}, mediaPlayers.stream().map(player => player.totalDurationProperty).toArray(size => new Array[ReadOnlyObjectProperty[Duration]](size)): _*)
 	}
 
 	/*
@@ -175,19 +215,15 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	 */
 
 	override def isPadLoaded: Boolean = {
-		mediaPlayers.isNotEmpty && !mediaPlayers.stream().anyMatch(player => player.mediaPlayer.getStatus == MediaPlayer.Status.UNKNOWN)
+		mediaPlayers.isNotEmpty
 	}
 
 	override def isLoaded(mediaPath: MediaPath): Boolean = {
-		val loadedOptional: Optional[Boolean] = mediaPlayers.stream()
-			.filter(item => item.path == mediaPath)
-			.findFirst()
-			.map(container => container.mediaPlayer.getStatus != MediaPlayer.Status.UNKNOWN)
-		loadedOptional.orElse(false)
+		true
 	}
 
 
-	override def isPadLoading: Boolean = mediaPlayers.stream().anyMatch(player => player.mediaPlayer.getStatus == MediaPlayer.Status.UNKNOWN)
+	override def isPadLoading: Boolean = false
 
 	/**
 	 * Load media files.
@@ -209,40 +245,19 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 			return
 		}
 
-		val media = new Media(path.toUri.toString)
-		val mediaPlayer = new MediaPlayer(media)
+		val duration = Duration.seconds(ContentPlayer.GetTotalDuration(path.toAbsolutePath.toString))
+		mediaPlayers.add(new ContentPlayerMediaContainer(this, mediaPath, duration))
 
-		mediaPlayer.setOnReady(() => {
+		Platform.runLater(() => {
 			getPad.setStatus(PadStatus.READY)
 
 			_durationProperty.bind(totalDurationBinding())
 			_positionProperty.set(Duration.ZERO)
 
-			Platform.runLater(() => {
-				if (getPad.isPadVisible) {
-					getPad.getController.getView.showBusyView(false)
-				}
-			})
-		})
-
-		mediaPlayer.errorProperty().addListener((_, _, newValue) => Platform.runLater(() => {
-			Logger.error(newValue)
-			pad.setStatus(PadStatus.ERROR)
-		}))
-		mediaPlayer.setOnError(() => Platform.runLater(() => {
 			if (getPad.isPadVisible) {
 				getPad.getController.getView.showBusyView(false)
 			}
-			Logger.error(mediaPlayer.getError)
-			pad.setStatus(PadStatus.ERROR)
-		}))
-
-		mediaPlayer.setOnEndOfMedia(() => {
-			getPad.setEof(true)
-			onEof()
 		})
-
-		mediaPlayers.add(new ContentPlayerMediaContainer(this, mediaPath, mediaPlayer))
 	}
 
 	/**
@@ -266,18 +281,12 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	 * @param mediaPath specify media path
 	 */
 	override def unloadMedia(mediaPath: MediaPath): Unit = {
-		val index = mediaPlayers.indexWhere(item => item.path.getId == mediaPath.getId)
-
-		if (index >= 0) {
-			val playerContainer = mediaPlayers(index)
-			playerContainer.stop()
-			mediaPlayers.remove(index)
-		}
+		mediaPlayers.removeIf(player => player.mediaPath == mediaPath)
 	}
 
 	override def reorderMedia(): Unit = {
 		val paths = pad.getPaths
-		mediaPlayers.sort((o1, o2) => Integer.compare(paths.indexOf(o1.path), paths.indexOf(o2.path)))
+		mediaPlayers.sort((o1, o2) => Integer.compare(paths.indexOf(o1.mediaPath), paths.indexOf(o2.mediaPath)))
 	}
 
 	/*
@@ -285,8 +294,7 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	 */
 
 	override def updateVolume(): Unit = {
-		val volume = VolumeManager.getInstance.computeVolume(getPad)
-		mediaPlayers.forEach(player => player.mediaPlayer.setVolume(volume))
+		// not needed
 	}
 
 	/**
@@ -305,18 +313,28 @@ class ContentPlayerPadContent(val pad: Pad, val `type`: String) extends PadConte
 	Custom Settings
 	 */
 
-	def shouldShowLastFrame(): Boolean = {
-		pad.getPadSettings.getCustomSettings.getOrDefault(ContentPlayerPadContentFactory.lastFrame, false).asInstanceOf[Boolean]
-	}
+	def shouldShowLastFrame(): Boolean = pad.getPadSettings.getCustomSettings.getOrDefault(ContentPlayerPadContentFactory.lastFrame, false).asInstanceOf[Boolean]
+
+	def isShuffle: Boolean = pad.getPadSettings.getCustomSettings.getOrDefault(Playlistable.SHUFFLE_SETTINGS_KEY, false).asInstanceOf[Boolean]
+
+	def isAutoNext: Boolean = pad.getPadSettings.getCustomSettings.getOrDefault(Playlistable.AUTO_NEXT_SETTINGS_KEY, false).asInstanceOf[Boolean]
 
 	def getSelectedZones: Seq[Zone] = {
-		val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration]
+		val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ContentPlayerPluginConfiguration]
 
 		val customSettings = pad.getPadSettings.getCustomSettings
-		val selectedZoneNames = customSettings.getOrDefault(
+		val selectedZoneIds = customSettings.getOrDefault(
 			ContentPlayerPadContentFactory.zones,
-			zoneConfiguration.zones.stream().map(zone => zone.getName).collect(Collectors.toList())
-		).asInstanceOf[util.List[String]]
-		zoneConfiguration.zones.asScala.filter(zone => selectedZoneNames.contains(zone.getName)).toSeq
+			zoneConfiguration.zones.stream().map(zone => zone.id).collect(Collectors.toList())
+		).asInstanceOf[util.List[UUID]]
+		zoneConfiguration.zones.asScala.filter(zone => selectedZoneIds.contains(zone.id)).toSeq
 	}
+
+	/*
+	Listener
+	 */
+
+	override def addPlaylistListener(listener: PlaylistListener): Unit = listeners.add(listener)
+
+	override def removePlaylistListener(listener: PlaylistListener): Unit = listeners.remove(listener)
 }
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala
index d08958d5da016d424a9204a12317b8fbade30f93..1ff33f5d92cf216d71279965a6f980678e591868 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentFactory.scala
@@ -1,12 +1,14 @@
 package de.tobias.playpad.plugin.content.pad
 
 import de.tobias.playpad.pad.Pad
-import de.tobias.playpad.pad.content.{PadContent, PadContentFactory}
+import de.tobias.playpad.pad.content.{PadContent, PadContentFactory, PadContentPlaylistFactory}
+import de.tobias.playpad.pad.mediapath.MediaPath
 import de.tobias.playpad.pad.view.IPadContentView
 import de.tobias.playpad.viewcontroller.PadSettingsTabViewController
+import javafx.scene.Node
 import javafx.scene.layout.Pane
 
-class ContentPlayerPadContentFactory(val `type`: String) extends PadContentFactory(`type`) {
+class ContentPlayerPadContentFactory(val `type`: String) extends PadContentFactory(`type`) with PadContentPlaylistFactory {
 
 	override def newInstance(pad: Pad): PadContent = new ContentPlayerPadContent(pad, getType)
 
@@ -15,10 +17,12 @@ class ContentPlayerPadContentFactory(val `type`: String) extends PadContentFacto
 	override def getSettingsViewController(pad: Pad): PadSettingsTabViewController = new ContentPlayerPadContentSettingsViewController(pad)
 
 	override def getSupportedTypes: Array[String] = ContentPlayerPadContentFactory.FILE_EXTENSION
+
+	override def getCustomPlaylistItemView(pad: Pad, mediaPath: MediaPath): Node = new ContentPlayerPlaylistView(pad, mediaPath)
 }
 
 object ContentPlayerPadContentFactory {
-	private val FILE_EXTENSION = Array("*.mp4", "*.mov")
+	private val FILE_EXTENSION = Array("*.mp4", "*.mkv", "*.mov")
 
 	val lastFrame = "ContentLastFrame"
 	val zones = "zones"
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala
index 14d5e0ee477047de962dfd5959c2ee67d0a9c1c8..4419b1e1677d33acd6fb1183e17f65bc4691d2a5 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadContentSettingsViewController.scala
@@ -5,7 +5,7 @@ import java.util
 import de.thecodelabs.utils.util.Localization
 import de.tobias.playpad.pad.Pad
 import de.tobias.playpad.plugin.content.ContentPluginMain
-import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration}
+import de.tobias.playpad.plugin.content.settings.{Zone, ContentPlayerPluginConfiguration}
 import de.tobias.playpad.profile.Profile
 import de.tobias.playpad.viewcontroller.PadSettingsTabViewController
 import javafx.beans.binding.Bindings
@@ -30,7 +30,7 @@ class ContentPlayerPadContentSettingsViewController(val pad: Pad) extends PadSet
 	load("view", "ContentPadSettings", Localization.getBundle)
 
 	override def init(): Unit = {
-		val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration]
+		val zoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ContentPlayerPluginConfiguration]
 		zoneListView.getItems.addAll(zoneConfiguration.zones)
 
 		addAllZonesButton.disableProperty().bind(Bindings.equal(Bindings.size(zoneListView.getCheckModel.getCheckedIndices), zoneListView.getItems.size()))
@@ -57,7 +57,7 @@ class ContentPlayerPadContentSettingsViewController(val pad: Pad) extends PadSet
 		val customSettings = pad.getPadSettings.getCustomSettings
 		customSettings.put(ContentPlayerPadContentFactory.lastFrame, lastFrameCheckbox.isSelected)
 
-		val selectedZoneNames = zoneListView.getCheckModel.getCheckedItems.asScala.map(zone => zone.getName)
+		val selectedZoneNames = zoneListView.getCheckModel.getCheckedItems.asScala.map(zone => zone.id)
 		customSettings.put(ContentPlayerPadContentFactory.zones, new util.ArrayList(selectedZoneNames.asJavaCollection))
 	}
 
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala
index 6ff298194c1ec57c2f6e7262b8c1c96db6498476..cbc98c236cfa37aef6f74c0390dfbbe78a4820dc 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPadPreview.scala
@@ -43,7 +43,8 @@ class ContentPlayerPadPreview(pad: Pad, parent: Pane) extends VBox with IPadCont
 	pad.getContent match {
 		case content: ContentPlayerPadContent =>
 			subTitleLabel.textProperty().bind(Bindings.createStringBinding(() => {
-				if (content.getCurrentPlayingMediaIndex < 0) "" else PathUtils.getFilenameWithoutExtension(pad.getPaths.get(content.getCurrentPlayingMediaIndex).getPath.getFileName)
+				if (content.getCurrentPlayingMediaIndex < 0) ""
+				else PathUtils.getFilenameWithoutExtension(content.getMediaContainers.get(content.getCurrentPlayingMediaIndex).getPath.getFileName)
 			}, content.currentPlayingMediaIndexProperty()))
 		case _ =>
 	}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPlaylistView.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPlaylistView.scala
new file mode 100644
index 0000000000000000000000000000000000000000..61d9a821db0a556daed3c9e4e5db61e9f32ba59c
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/pad/ContentPlayerPlaylistView.scala
@@ -0,0 +1,57 @@
+package de.tobias.playpad.plugin.content.pad
+
+import de.thecodelabs.utils.threading.Worker
+import de.thecodelabs.utils.util.Localization
+import de.tobias.playpad.pad.Pad
+import de.tobias.playpad.pad.mediapath.MediaPath
+import de.tobias.playpad.plugin.content.util.FfmpegUtils
+import javafx.application.Platform
+import javafx.event.ActionEvent
+import javafx.geometry.Pos
+import javafx.scene.control.{Button, Label, ProgressIndicator, Separator}
+import javafx.scene.layout.{HBox, VBox}
+
+class ContentPlayerPlaylistView(pad: Pad, mediaPath: MediaPath) extends VBox {
+
+	val progressIndicator = new ProgressIndicator()
+	val sizeLabel: Label = new Label()
+	val convertButton = new Button(Localization.getString("plugin.content.player.playlist.convert"))
+
+	private val sizeTextLabel = new Label(Localization.getString("plugin.content.player.playlist.size"))
+	private val sizeBox = new HBox(sizeTextLabel, sizeLabel)
+
+	sizeTextLabel.setMinWidth(150)
+	sizeBox.setSpacing(14)
+
+	convertButton.setOnAction(onConvertVStackButton)
+	progressIndicator.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS)
+	progressIndicator.setPrefSize(30, 30)
+
+	private val actionBox = new HBox(convertButton, progressIndicator)
+	actionBox.setAlignment(Pos.CENTER_LEFT)
+	actionBox.setSpacing(14)
+
+	private val separator = new Separator()
+
+	getChildren.addAll(separator, sizeBox, actionBox)
+	setSpacing(14)
+
+	Worker.runLater(() => {
+		val resolution = FfmpegUtils.getResolution(mediaPath.getPath)
+		Platform.runLater(() => {
+			sizeLabel.setText(f"${resolution.getKey} x ${resolution.getValue}")
+			progressIndicator.setVisible(false)
+		})
+	})
+
+
+	private def onConvertVStackButton(event: ActionEvent): Unit = {
+		progressIndicator.setVisible(true)
+		Worker.runLater(() => {
+			FfmpegUtils.convertMediaVStack(mediaPath.getPath)
+			Platform.runLater(() => {
+				progressIndicator.setVisible(false)
+			})
+		})
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerBinding.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerBinding.scala
new file mode 100644
index 0000000000000000000000000000000000000000..9484ac9be1ede9d096d580c4fdd3b96e25756429
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerBinding.scala
@@ -0,0 +1,83 @@
+package de.tobias.playpad.plugin.content.player
+
+import de.tobias.playpad.plugin.content.pad.ContentPlayerMediaContainer
+import de.tobias.playpad.plugin.content.settings.Zone
+import javafx.application.Platform
+import javafx.beans.property.{ObjectProperty, SimpleObjectProperty}
+import javafx.util.Duration
+import nativecontentplayerwindows.ContentPlayer
+
+class ContentPlayerBinding(val nativePlayer: ContentPlayer, val zone: Zone) {
+
+	private val positionProperty: ObjectProperty[Duration] = new SimpleObjectProperty[Duration](Duration.ZERO)
+	private val durationProperty: ObjectProperty[Duration] = new SimpleObjectProperty[Duration](Duration.ZERO)
+	private val currentMedia: ObjectProperty[ContentPlayerMediaContainer] = new SimpleObjectProperty[ContentPlayerMediaContainer]()
+
+	nativePlayer.setContentPlayerStopListener(endOfFile => {
+		if (endOfFile && currentMedia.get() != null) {
+			currentMedia.get().content.pad.setEof(true)
+			currentMedia.get().content.onEof()
+		}
+	})
+	nativePlayer.setContentPlayerPositionListener((position, total) => {
+		Platform.runLater(() => {
+			val totalDuration = Duration.seconds(total)
+			if (totalDuration != durationProperty.get()) {
+				durationProperty.setValue(totalDuration)
+			}
+			positionProperty.setValue(Duration.seconds(position))
+		})
+	})
+	currentMedia.addListener((_, oldValue, newValue) => {
+		if (oldValue != null) {
+			oldValue.content._positionProperty.unbind()
+			oldValue.content._durationProperty.unbind()
+		}
+		if (newValue != null) {
+			newValue.content._positionProperty.bind(positionProperty)
+			newValue.content._durationProperty.bind(durationProperty)
+		}
+	})
+
+	def play(media: ContentPlayerMediaContainer, withFadeIn: Boolean): Unit = {
+		if (currentMedia.get() != null && currentMedia.get().content.getPad != media.content.getPad) {
+			if (currentMedia.get().content.getPad.isPlay) {
+				// Stop the current playing media on this player and hold the last frame
+				currentMedia.get().content.stopMediaByOtherPlayer = true
+				currentMedia.get().content.getPad.stop()
+			} else if (currentMedia.get().content.getPad.isPaused) {
+				// The player must be resumed before playing the next media
+				nativePlayer.Resume(withFadeIn)
+				currentMedia.get().content.getPad.stop()
+			}
+		}
+		nativePlayer.Play(media.getPath.toString, withFadeIn)
+		currentMedia.set(media)
+	}
+
+	def resume(media: ContentPlayerMediaContainer, withFadeIn: Boolean): Unit = {
+		nativePlayer.Resume(withFadeIn)
+		currentMedia.set(media)
+	}
+
+	def pause(media: ContentPlayerMediaContainer): Unit = nativePlayer.Pause()
+
+	def stop(media: ContentPlayerMediaContainer): Unit = {
+		// If media is stopped by a different pad, the current media should keep playing to have a smooth transition
+		// to the new media. Otherwise the media will be stopped normally.
+		if (!media.content.stopMediaByOtherPlayer) {
+			nativePlayer.Stop()
+			currentMedia.set(null)
+		}
+	}
+
+	def clearHold(): Unit = nativePlayer.ClearHold()
+
+	def highlight(on: Boolean): Unit = nativePlayer.HighlightPlayer(on)
+
+	def setFadeValue(value: Double): Unit = nativePlayer.Fade(value)
+
+	def clear(): Unit = {
+		currentMedia.set(null)
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala
deleted file mode 100644
index c659e296cd880469fcae5e27ca2f5b193fad0258..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerViewController.scala
+++ /dev/null
@@ -1,118 +0,0 @@
-package de.tobias.playpad.plugin.content.player
-
-import de.thecodelabs.logger.Logger
-import de.thecodelabs.utils.ui.size.IgnoreStageSizing
-import de.thecodelabs.utils.ui.{NVC, NVCStage}
-import de.tobias.playpad.PlayPadPlugin
-import de.tobias.playpad.plugin.content.settings.{Zone, ZoneConfiguration}
-import de.tobias.playpad.project.page.PadIndex
-import javafx.geometry.Insets
-import javafx.scene.layout._
-import javafx.scene.media.MediaPlayer
-import javafx.scene.paint.Color
-import javafx.stage.{Stage, StageStyle}
-
-import scala.collection.mutable.ListBuffer
-
-@IgnoreStageSizing
-class ContentPlayerViewController extends NVC {
-
-	private val mediaStacks: ListBuffer[MediaPlayerStack] = ListBuffer.empty
-
-	load("view", "PlayerView")
-	private val stageContainer: NVCStage = applyViewControllerToStage
-	stageContainer.addCloseHook(() => false)
-
-	Logger.debug("Create Player View Controller")
-
-	override def init(): Unit = {
-		val parent = getParent.asInstanceOf[Pane]
-		parent.setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)))
-	}
-
-	override def initStage(stage: Stage): Unit = {
-		super.initStage(stage)
-		stage.initStyle(StageStyle.UNDECORATED)
-		stage.setAlwaysOnTop(true)
-
-		stage.getScene.setFill(Color.BLACK)
-		stage.getIcons.add(PlayPadPlugin.getInstance().getIcon)
-	}
-
-	def showMediaPlayer(padIndex: PadIndex, mediaPlayer: MediaPlayer, zones: Seq[Zone]): Unit = {
-		val iterator = this.mediaStacks.iterator
-		while (iterator.hasNext) {
-			val mediaPlayerStack = iterator.next()
-			if (zones.contains(mediaPlayerStack.zone)) {
-				mediaPlayerStack.showMediaPlayer(padIndex, mediaPlayer)
-			}
-		}
-	}
-
-	def disconnectMediaPlayer(mediaPlayer: MediaPlayer, zones: Seq[Zone]): Unit = {
-		val iterator = this.mediaStacks.iterator
-		while (iterator.hasNext) {
-			val mediaPlayerStack = iterator.next()
-			if (zones.contains(mediaPlayerStack.zone)) {
-				mediaPlayerStack.disconnectMediaPlayer(mediaPlayer)
-			}
-		}
-	}
-
-	def configurePlayers(configuration: ZoneConfiguration): Unit = {
-		if (configuration.zones.isEmpty) {
-			closeStage()
-			return
-		}
-
-		val parent = getParent.asInstanceOf[Pane]
-		parent.getChildren.clear()
-
-		mediaStacks.clear()
-		configuration.zones.forEach(player => {
-			val mediaPlayerStack = new MediaPlayerStack(player)
-			mediaStacks.addOne(mediaPlayerStack)
-			parent.getChildren.add(mediaPlayerStack)
-		})
-
-		showStage()
-
-		getStageContainer.ifPresent(container => {
-			val stage = container.getStage
-
-			import scala.jdk.CollectionConverters._
-			val zones = configuration.zones.asScala
-			val maxWidth = zones.map(player => player.x + player.width).max
-			val maxHeight = zones.map(player => player.y + player.height).max
-
-			stage.setX(0)
-			stage.setY(0)
-			stage.setWidth(maxWidth)
-			stage.setHeight(maxHeight)
-		})
-	}
-
-	def addActivePadToList(padIndex: PadIndex, zones: Seq[Zone]): Unit = getMediaStacks(zones)
-		.foreach(mediaStack => mediaStack.addActivePad(padIndex))
-
-	def removeActivePadFromList(padIndex: PadIndex, zones: Seq[Zone]): Unit = getMediaStacks(zones)
-		.foreach(mediaStack => mediaStack.removeActivePad(padIndex))
-
-	def highlight(zone: Zone, on: Boolean): Unit = {
-		if (getMediaStack(zone).isEmpty) {
-			return
-		}
-		getMediaStack(zone).head.highlight(on)
-	}
-
-	def setFadeValue(mediaPlayer: MediaPlayer, zones: Seq[Zone], value: Double): Unit = getMediaStacks(zones)
-		.foreach(mediaStack => mediaStack.setFadeValue(mediaPlayer, value))
-
-	private def getMediaStack(zone: Zone): ListBuffer[MediaPlayerStack] = {
-		getMediaStacks(List(zone))
-	}
-
-	private def getMediaStacks(zones: Seq[Zone]): ListBuffer[MediaPlayerStack] = {
-		mediaStacks.filter(mediaPlayer => zones.contains(mediaPlayer.zone))
-	}
-}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerWindowController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerWindowController.scala
new file mode 100644
index 0000000000000000000000000000000000000000..ca8842e8a02b4daddf8cb248f4461b08c986af83
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/ContentPlayerWindowController.scala
@@ -0,0 +1,97 @@
+package de.tobias.playpad.plugin.content.player
+
+import de.thecodelabs.logger.Logger
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.content.pad.ContentPlayerMediaContainer
+import de.tobias.playpad.plugin.content.settings.{ContentPlayerPluginConfiguration, ContentPlayerSettingsViewController, Zone}
+import nativecontentplayerwindows.{ContentPlayer, ContentPlayerWindow}
+
+import scala.collection.mutable.ListBuffer
+
+class ContentPlayerWindowController {
+
+	var window: ContentPlayerWindow = _
+	val players: ListBuffer[ContentPlayerBinding] = ListBuffer.empty
+
+	def configurePlayers(configuration: ContentPlayerPluginConfiguration): Unit = {
+		players.foreach(_.clear())
+		players.clear()
+		if (window != null) {
+			window.Close()
+		}
+
+		import scala.jdk.CollectionConverters._
+		val zones = configuration.zones.asScala
+
+		if (zones.isEmpty) {
+			return
+		}
+
+		window = new ContentPlayerWindow()
+		window.SetIcon(PlayPadPlugin.getInstance.getIconData)
+		window.Show()
+
+		val minX = zones.map(player => player.x).min.toInt
+		val minY = zones.map(player => player.y).min.toInt
+
+		zones.foreach(zone => {
+			val contentPlayer = new ContentPlayer(zone.move(-minX, -minY).toNative)
+			window.AddContentPlayer(contentPlayer)
+
+			players.addOne(new ContentPlayerBinding(contentPlayer, zone))
+		})
+
+		val maxWidth = zones.map(player => player.x + player.width - minX).max.toInt
+		val maxHeight = zones.map(player => player.y + player.height - minY).max.toInt
+
+		val screens = ContentPlayerWindow.GetScreens
+		val selectedScreen = ContentPlayerSettingsViewController.getZoneConfiguration.screen
+		val screen = screens.find(screen => screen.getName == selectedScreen)
+		  .getOrElse(screens.head)
+
+		window.SetSize(maxWidth, maxHeight)
+		window.SetScreen(screen)
+		window.SetLocation(minX, minY)
+	}
+
+	private def getContentPlayerBinding(zone: Zone): ContentPlayerBinding = {
+		val zones = getContentPlayerBindings(List(zone))
+		if (zones.nonEmpty) zones.head else null
+	}
+
+	private def getContentPlayerBindings(zones: Seq[Zone]): ListBuffer[ContentPlayerBinding] = {
+		players.filter(mediaPlayer => zones.contains(mediaPlayer.zone))
+	}
+
+	def play(media: ContentPlayerMediaContainer, withFadeIn: Boolean): Unit = {
+		getContentPlayerBindings(media.content.getSelectedZones).foreach(player => player.play(media, withFadeIn))
+	}
+
+	def resume(media: ContentPlayerMediaContainer, withFadeIn: Boolean): Unit = {
+		getContentPlayerBindings(media.content.getSelectedZones).foreach(player => player.resume(media, withFadeIn))
+	}
+
+	def pause(media: ContentPlayerMediaContainer): Unit = {
+		getContentPlayerBindings(media.content.getSelectedZones).foreach(player => player.pause(media))
+	}
+
+	def stop(media: ContentPlayerMediaContainer): Unit = {
+		getContentPlayerBindings(media.content.getSelectedZones).foreach(player => player.stop(media))
+	}
+
+	def clearHold(media: ContentPlayerMediaContainer): Unit = {
+		Logger.debug(s"Clear last frame for pad ${media.content.pad.getPadIndex}")
+		getContentPlayerBindings(media.content.getSelectedZones).foreach(player => player.clearHold())
+	}
+
+	def setFadeValue(zones: Seq[Zone], value: Double): Unit = {
+		getContentPlayerBindings(zones).foreach(player => player.setFadeValue(value))
+	}
+
+	def highlight(zone: Zone, on: Boolean): Unit = {
+		if (getContentPlayerBinding(zone) == null) {
+			return
+		}
+		getContentPlayerBinding(zone).highlight(on)
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala
deleted file mode 100644
index 15d3872130e3fe4b1d32c3ff3d67f11329a70c6d..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/player/MediaPlayerStack.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-package de.tobias.playpad.plugin.content.player
-
-import java.util.stream.Collectors
-
-import de.thecodelabs.logger.Logger
-import de.tobias.playpad.plugin.content.settings.Zone
-import de.tobias.playpad.project.page.PadIndex
-import javafx.geometry.Insets
-import javafx.scene.layout.{Background, BackgroundFill, CornerRadii, StackPane}
-import javafx.scene.media.{MediaPlayer, MediaView}
-import javafx.scene.paint.Color
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-class MediaPlayerStack(val zone: Zone) extends StackPane {
-
-	private var activePlayers: ListBuffer[PadIndex] = ListBuffer.empty
-	val mediaViews: mutable.Map[MediaPlayer, MediaView] = new mutable.HashMap[MediaPlayer, MediaView]()
-
-	setLayoutX(zone.x)
-	setLayoutY(zone.y)
-	setMinWidth(zone.width)
-	setMaxWidth(zone.width)
-	setMinHeight(zone.height)
-	setMaxHeight(zone.height)
-
-	def addActivePad(padIndex: PadIndex): Unit = activePlayers.addOne(padIndex)
-
-	def removeActivePad(padIndex: PadIndex): Unit = activePlayers = activePlayers.filter(element => element != padIndex)
-
-	def showMediaPlayer(padIndex: PadIndex, mediaPlayer: MediaPlayer): Unit = {
-		if (!mediaViews.contains(mediaPlayer)) {
-			val mediaView = new MediaView(mediaPlayer)
-			mediaView.setFitWidth(zone.width)
-			mediaView.setFitHeight(zone.height)
-			mediaViews.put(mediaPlayer, mediaView)
-		}
-
-		val mediaView = mediaViews(mediaPlayer)
-		mediaView.setUserData(padIndex)
-		mediaView.setOpacity(1.0)
-
-		if (!getChildren.contains(mediaView)) {
-			val index = activePlayers.indexOf(padIndex)
-			try {
-				getChildren.add(index, mediaView)
-			} catch {
-				case e: Exception =>
-					Logger.error(e)
-					getChildren.add(mediaView)
-			}
-		}
-	}
-
-	def disconnectMediaPlayer(mediaPlayer: MediaPlayer): Unit = {
-		if (mediaViews.contains(mediaPlayer)) {
-			getChildren.remove(mediaViews(mediaPlayer))
-		}
-	}
-
-	def setFadeValue(mediaPlayer: MediaPlayer, value: Double): Unit = {
-		if (mediaViews.contains(mediaPlayer)) {
-			val mediaView = mediaViews(mediaPlayer)
-			mediaView.setOpacity(value)
-		}
-	}
-
-	def highlight(on: Boolean): Unit = {
-		if (on) {
-			setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)))
-		} else {
-			setBackground(null)
-		}
-	}
-
-	override def toString: String = f"MediaPlayerStack: ${getChildren.stream().map(view => f"MediaView: ${view.getUserData}").collect(Collectors.joining(", "))}"
-}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerPluginConfiguration.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerPluginConfiguration.scala
new file mode 100644
index 0000000000000000000000000000000000000000..febbe097823de5f48a1b9f98abf4e950ad9ddd4f
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerPluginConfiguration.scala
@@ -0,0 +1,76 @@
+package de.tobias.playpad.plugin.content.settings
+
+import java.util
+import java.util.{UUID, List => JavaList}
+import de.thecodelabs.storage.settings.annotation.{FilePath, Key}
+import de.tobias.playpad.Displayable
+import javafx.beans.property.{SimpleStringProperty, StringProperty}
+
+@FilePath("players.json")
+class ContentPlayerPluginConfiguration {
+	@Key
+	var screen: String = _
+	@Key
+	var zones: JavaList[Zone] = new util.ArrayList[Zone]()
+	@Key
+	var ffmpegExecutable: String = _
+	@Key
+	var ffprobeExecutable: String = _
+}
+
+class Zone extends Displayable {
+	@Key
+	var id: UUID = UUID.randomUUID()
+	@Key
+	private var name: String = _
+	@Key
+	var x: Double = _
+	@Key
+	var y: Double = _
+	@Key
+	var width: Double = _
+	@Key
+	var height: Double = _
+
+	def getName: String = name
+
+	def setName(name: String): Unit = {
+		this.name = name
+		_displayProperty.set(name)
+	}
+
+	def move(x: Int, y: Int): Zone = {
+		val zone = new Zone()
+		zone.name = this.name
+		zone.x = this.x + x
+		zone.y = this.y + y
+		zone.width = this.width
+		zone.height = this.height
+		zone
+	}
+
+	def toNative: nativecontentplayerwindows.Zone = new nativecontentplayerwindows.Zone(x.toInt, y.toInt, width.toInt, height.toInt)
+
+	private val _displayProperty: StringProperty = new SimpleStringProperty(name)
+
+	override def displayProperty(): StringProperty = {
+		_displayProperty.set(name)
+		_displayProperty
+	}
+
+	override def toString: String = name
+
+	def canEqual(other: Any): Boolean = other.isInstanceOf[Zone]
+
+	override def equals(other: Any): Boolean = other match {
+		case that: Zone =>
+			(that canEqual this) &&
+			  name == that.name
+		case _ => false
+	}
+
+	override def hashCode(): Int = {
+		val state = Seq(name, x, y, width, height)
+		state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerSettingsViewController.scala
new file mode 100644
index 0000000000000000000000000000000000000000..232afcb4edf0fa98c5b8bbdd3a7236d8b2efa6dc
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentPlayerSettingsViewController.scala
@@ -0,0 +1,219 @@
+package de.tobias.playpad.plugin.content.settings
+
+import de.thecodelabs.utils.ui.icon.{FontAwesomeType, FontIcon}
+import de.thecodelabs.utils.ui.scene.input.NumberTextField
+import de.thecodelabs.utils.util.Localization
+import de.tobias.playpad.plugin.content.ContentPluginMain
+import de.tobias.playpad.plugin.content.settings.ContentPlayerSettingsViewController.SelectableContentScreen
+import de.tobias.playpad.profile.{Profile, ProfileSettings}
+import de.tobias.playpad.project.Project
+import de.tobias.playpad.viewcontroller.main.IMainViewController
+import de.tobias.playpad.viewcontroller.option.{IProfileReloadTask, ProfileSettingsTabViewController}
+import javafx.application.Platform
+import javafx.event.ActionEvent
+import javafx.fxml.FXML
+import javafx.scene.control._
+import javafx.stage.FileChooser
+import nativecontentplayerwindows.{ContentPlayerWindow, ContentScreen}
+
+import java.awt.Desktop
+import java.net.URI
+
+class ContentPlayerSettingsViewController extends ProfileSettingsTabViewController with IProfileReloadTask {
+
+	private var startZoneHash: Int = _
+	private var startScreenHash: Int = _
+
+	@FXML var ffmpegButton: Button = _
+	@FXML var ffmpegTextField: TextField = _
+	@FXML var ffprobeButton: Button = _
+	@FXML var ffprobeTextField: TextField = _
+	@FXML var ffmpegDownloadLink: Hyperlink = _
+
+	@FXML
+	var screenComboBox: ComboBox[SelectableContentScreen] = _
+	@FXML
+	var listView: ListView[Zone] = _
+
+	@FXML
+	var nameTextField: TextField = _
+	@FXML
+	var xTextField: NumberTextField = _
+	@FXML
+	var yTextField: NumberTextField = _
+	@FXML
+	var widthTextField: NumberTextField = _
+	@FXML
+	var heightTextField: NumberTextField = _
+
+	@FXML
+	var addButton: Button = _
+	@FXML
+	var removeButton: Button = _
+
+	load("view", "ContentPlayerSettingsTab", Localization.getBundle)
+
+	override def init(): Unit = {
+		listView.setCellFactory((_: ListView[Zone]) => new ListCell[Zone] {
+			override def updateItem(item: Zone, empty: Boolean): Unit = {
+				super.updateItem(item, empty)
+				if (!empty) {
+					textProperty().bind(item.displayProperty())
+				} else {
+					textProperty().unbind()
+					setText("")
+				}
+			}
+		})
+		listView.getSelectionModel.selectedItemProperty().addListener((_, oldValue, newValue) => {
+			val playerViewController = ContentPluginMain.playerViewController
+
+			if (oldValue != null) {
+				saveSettingsToZone(oldValue)
+				playerViewController.highlight(oldValue, on = false)
+			}
+			if (newValue != null) {
+				showSettingsOfZone(newValue)
+				playerViewController.highlight(newValue, on = true)
+			} else {
+				clearTextFields()
+			}
+		})
+
+		val screens = ContentPlayerWindow.GetScreens
+		screenComboBox.getItems.setAll(screens.map(screen => new SelectableContentScreen(screen)): _*)
+		screenComboBox.setCellFactory((_: ListView[SelectableContentScreen]) => new ContentScreenCell())
+		screenComboBox.setButtonCell(new ContentScreenCell())
+
+		ffmpegButton.setGraphic(new FontIcon(FontAwesomeType.FOLDER))
+		ffprobeButton.setGraphic(new FontIcon(FontAwesomeType.FOLDER))
+
+		ffmpegDownloadLink.setText(Localization.getString("plugin.content.player.settings.ffmpeg_link"))
+	}
+
+	private def saveSettingsToZone(zone: Zone): Unit = {
+		zone.setName(nameTextField.getText)
+		zone.x = xTextField.getText.toDouble
+		zone.y = yTextField.getText.toDouble
+		zone.width = widthTextField.getText.toDouble
+		zone.height = heightTextField.getText.toDouble
+	}
+
+	private def showSettingsOfZone(zone: Zone): Unit = {
+		nameTextField.setText(zone.getName)
+		xTextField.setText(zone.x.toInt.toString)
+		yTextField.setText(zone.y.toInt.toString)
+		widthTextField.setText(zone.width.toInt.toString)
+		heightTextField.setText(zone.height.toInt.toString)
+	}
+
+	private def clearTextFields(): Unit = {
+		nameTextField.setText("")
+		xTextField.setText("")
+		yTextField.setText("")
+		widthTextField.setText("")
+		heightTextField.setText("")
+	}
+
+	// Actions
+	@FXML def onFfmpegHandle(event: ActionEvent): Unit = {
+		val chooser = new FileChooser()
+		val selected = chooser.showOpenDialog(getContainingWindow)
+		if (selected != null) {
+			ffmpegTextField.setText(selected.toPath.toAbsolutePath.toString)
+		}
+	}
+
+	@FXML def onFfprobeHandle(event: ActionEvent): Unit = {
+		val chooser = new FileChooser()
+		val selected = chooser.showOpenDialog(getContainingWindow)
+		if (selected != null) {
+			ffprobeTextField.setText(selected.toPath.toAbsolutePath.toString)
+		}
+	}
+
+	@FXML def onFfmpegDownloadLink(event: ActionEvent): Unit = {
+		Desktop.getDesktop.browse(URI.create(ffmpegDownloadLink.getText))
+	}
+
+	@FXML
+	def onAddHandle(): Unit = {
+		val newConfiguration = new Zone
+		newConfiguration.setName(Localization.getString("plugin.content.player.settings.default_name"))
+
+		ContentPlayerSettingsViewController.getZoneConfiguration.zones.add(newConfiguration)
+		listView.getItems.add(newConfiguration)
+	}
+
+	@FXML
+	def onRemoveHandle(): Unit = {
+		val selectedItem = listView.getSelectionModel.getSelectedItem
+		if (selectedItem != null) {
+			listView.getItems.remove(selectedItem)
+			ContentPlayerSettingsViewController.getZoneConfiguration.zones.remove(selectedItem)
+		}
+	}
+
+	override def loadSettings(settings: Profile): Unit = {
+		val configuration = ContentPlayerSettingsViewController.getZoneConfiguration
+		startZoneHash = configuration.zones.hashCode
+		startScreenHash = configuration.screen.hashCode
+
+		listView.getItems.setAll(configuration.zones)
+
+		val screens = ContentPlayerWindow.GetScreens
+		val selectedScreen = configuration.screen
+		screenComboBox.getSelectionModel.select(new SelectableContentScreen(screens
+		  .find(screen => screen.getName == selectedScreen)
+		  .getOrElse(screens.head)))
+
+		ffmpegTextField.setText(configuration.ffmpegExecutable)
+		ffprobeTextField.setText(configuration.ffprobeExecutable)
+	}
+
+	override def saveSettings(settings: Profile): Unit = {
+		val configuration = ContentPlayerSettingsViewController.getZoneConfiguration
+
+		val selectedItem = listView.getSelectionModel.getSelectedItem
+		if (selectedItem != null) {
+			saveSettingsToZone(selectedItem)
+		}
+		val selectedScreen = screenComboBox.getSelectionModel.getSelectedItem
+		if (selectedScreen != null) {
+			configuration.screen = selectedScreen.getName
+		}
+
+		configuration.ffmpegExecutable = ffmpegTextField.getText
+		configuration.ffprobeExecutable = ffprobeTextField.getText
+	}
+
+	override def needReload(): Boolean = startZoneHash != ContentPlayerSettingsViewController.getZoneConfiguration.zones.hashCode ||
+	  startScreenHash != ContentPlayerSettingsViewController.getZoneConfiguration.screen.hashCode ||
+	  !listView.getSelectionModel.isEmpty
+
+	override def validSettings(): Boolean = {
+		true
+	}
+
+	override def name(): String = Localization.getString("plugin.content.player.settings")
+
+
+	override def getTask(settings: ProfileSettings, project: Project, controller: IMainViewController): Runnable = () =>
+		Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(ContentPlayerSettingsViewController.getZoneConfiguration))
+
+}
+
+object ContentPlayerSettingsViewController {
+	private[settings] class SelectableContentScreen(val contentScreen: ContentScreen) {
+		override def equals(obj: Any): Boolean = {
+			if (!obj.isInstanceOf[SelectableContentScreen]) {
+				return false
+			}
+			contentScreen.getName == obj.asInstanceOf[SelectableContentScreen].getName
+		}
+
+		def getName: String = contentScreen.getName
+	}
+
+	def getZoneConfiguration: ContentPlayerPluginConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ContentPlayerPluginConfiguration]
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentScreenCell.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentScreenCell.scala
new file mode 100644
index 0000000000000000000000000000000000000000..7982d239ab01d6b614b36766a79f640ca444bcb6
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ContentScreenCell.scala
@@ -0,0 +1,15 @@
+package de.tobias.playpad.plugin.content.settings
+
+import de.tobias.playpad.plugin.content.settings.ContentPlayerSettingsViewController.SelectableContentScreen
+import javafx.scene.control.ListCell
+
+class ContentScreenCell extends ListCell[SelectableContentScreen] {
+	override def updateItem(item: SelectableContentScreen, empty: Boolean): Unit = {
+		super.updateItem(item, empty)
+		if (!empty) {
+			setText(item.getName)
+		} else {
+			setText("")
+		}
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala
deleted file mode 100644
index 956a0183c9eafa8245a6565558bf85aeaa176e4c..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneConfiguration.scala
+++ /dev/null
@@ -1,43 +0,0 @@
-package de.tobias.playpad.plugin.content.settings
-
-import java.util
-import java.util.{List => JavaList}
-
-import de.thecodelabs.storage.settings.annotation.{FilePath, Key}
-import de.tobias.playpad.Displayable
-import javafx.beans.property.{SimpleStringProperty, StringProperty}
-
-@FilePath("players.json")
-class ZoneConfiguration {
-	@Key
-	var zones: JavaList[Zone] = new util.ArrayList[Zone]()
-}
-
-class Zone extends Displayable {
-	@Key
-	private var name: String = _
-	@Key
-	var x: Double = _
-	@Key
-	var y: Double = _
-	@Key
-	var width: Double = _
-	@Key
-	var height: Double = _
-
-	def getName: String = name
-
-	def setName(name: String): Unit = {
-		this.name = name
-		_displayProperty.set(name)
-	}
-
-	private val _displayProperty: StringProperty = new SimpleStringProperty(name)
-
-	override def displayProperty(): StringProperty = {
-		_displayProperty.set(name)
-		_displayProperty
-	}
-
-	override def toString: String = name
-}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala
deleted file mode 100644
index bae668ff818ef49c8b463df091ccea02bffdb6f4..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/settings/ZoneSettingsViewController.scala
+++ /dev/null
@@ -1,134 +0,0 @@
-package de.tobias.playpad.plugin.content.settings
-
-import de.thecodelabs.utils.ui.scene.input.NumberTextField
-import de.thecodelabs.utils.util.Localization
-import de.tobias.playpad.plugin.content.ContentPluginMain
-import de.tobias.playpad.profile.{Profile, ProfileSettings}
-import de.tobias.playpad.project.Project
-import de.tobias.playpad.viewcontroller.main.IMainViewController
-import de.tobias.playpad.viewcontroller.option.{IProfileReloadTask, ProfileSettingsTabViewController}
-import javafx.application.Platform
-import javafx.fxml.FXML
-import javafx.scene.control.{Button, ListCell, ListView, TextField}
-
-class ZoneSettingsViewController extends ProfileSettingsTabViewController with IProfileReloadTask {
-
-	@FXML
-	var listView: ListView[Zone] = _
-
-	@FXML
-	var nameTextField: TextField = _
-	@FXML
-	var xTextField: NumberTextField = _
-	@FXML
-	var yTextField: NumberTextField = _
-	@FXML
-	var widthTextField: NumberTextField = _
-	@FXML
-	var heightTextField: NumberTextField = _
-
-	@FXML
-	var addButton: Button = _
-	@FXML
-	var removeButton: Button = _
-
-	load("view", "ZoneSettings", Localization.getBundle)
-
-	override def init(): Unit = {
-		listView.setCellFactory((_: ListView[Zone]) => new ListCell[Zone] {
-			override def updateItem(item: Zone, empty: Boolean): Unit = {
-				super.updateItem(item, empty)
-				if (!empty) {
-					textProperty().bind(item.displayProperty())
-				} else {
-					textProperty().unbind()
-					setText("")
-				}
-			}
-		})
-		listView.getSelectionModel.selectedItemProperty().addListener((_, oldValue, newValue) => {
-			val playerViewController = ContentPluginMain.playerViewController
-
-			if (oldValue != null) {
-				saveSettingsToZone(oldValue)
-				playerViewController.highlight(oldValue, on = false)
-			}
-			if (newValue != null) {
-				showSettingsOfZone(newValue)
-				playerViewController.highlight(newValue, on = true)
-			} else {
-				clearTextFields()
-			}
-		})
-	}
-
-	private def saveSettingsToZone(zone: Zone): Unit = {
-		zone.setName(nameTextField.getText)
-		zone.x = xTextField.getText.toDouble
-		zone.y = yTextField.getText.toDouble
-		zone.width = widthTextField.getText.toDouble
-		zone.height = heightTextField.getText.toDouble
-	}
-
-	private def showSettingsOfZone(zone: Zone): Unit = {
-		nameTextField.setText(zone.getName)
-		xTextField.setText(zone.x.toInt.toString)
-		yTextField.setText(zone.y.toInt.toString)
-		widthTextField.setText(zone.width.toInt.toString)
-		heightTextField.setText(zone.height.toInt.toString)
-	}
-
-	private def clearTextFields(): Unit = {
-		nameTextField.setText("")
-		xTextField.setText("")
-		yTextField.setText("")
-		widthTextField.setText("")
-		heightTextField.setText("")
-	}
-
-	// Actions
-	@FXML
-	def onAddHandle(): Unit = {
-		val newConfiguration = new Zone
-		newConfiguration.setName(Localization.getString("plugin.content.player.settings.default_name"))
-
-		getZoneConfiguration.zones.add(newConfiguration)
-		listView.getItems.add(newConfiguration)
-	}
-
-	@FXML
-	def onRemoveHandle(): Unit = {
-		val selectedItem = listView.getSelectionModel.getSelectedItem
-		if (selectedItem != null) {
-			listView.getItems.remove(selectedItem)
-			getZoneConfiguration.zones.remove(selectedItem)
-		}
-	}
-
-	override def loadSettings(settings: Profile): Unit = {
-		listView.getItems.setAll(getZoneConfiguration.zones)
-	}
-
-	override def saveSettings(settings: Profile): Unit = {
-		val selectedItem = listView.getSelectionModel.getSelectedItem
-		if (selectedItem != null) {
-			saveSettingsToZone(selectedItem)
-		}
-	}
-
-	override def needReload(): Boolean = {
-		true
-	}
-
-	override def validSettings(): Boolean = {
-		true
-	}
-
-	override def name(): String = Localization.getString("plugin.content.player.settings")
-
-
-	override def getTask(settings: ProfileSettings, project: Project, controller: IMainViewController): Runnable = () =>
-		Platform.runLater(() => ContentPluginMain.playerViewController.configurePlayers(getZoneConfiguration))
-
-	private def getZoneConfiguration: ZoneConfiguration = Profile.currentProfile().getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ZoneConfiguration]
-}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/FfmpegUtils.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/FfmpegUtils.scala
new file mode 100644
index 0000000000000000000000000000000000000000..548b5531381752ca1bae827ec4173517a8486761
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/FfmpegUtils.scala
@@ -0,0 +1,79 @@
+package de.tobias.playpad.plugin.content.util
+
+import de.thecodelabs.logger.Logger
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.content.ContentPluginMain
+import de.tobias.playpad.plugin.content.settings.ContentPlayerPluginConfiguration
+import de.tobias.playpad.profile.Profile
+import javafx.util.Pair
+import net.bramp.ffmpeg.builder.FFmpegBuilder
+import net.bramp.ffmpeg.{FFmpeg, FFmpegExecutor, FFprobe}
+import org.apache.commons.lang3.StringUtils
+
+import java.nio.file.{Files, Path}
+
+object FfmpegUtils {
+
+	private var ffmpeg: FFmpeg = _
+	private var ffprobe: FFprobe = _
+
+	def initialize(): Unit = {
+		Logger.debug("Initialize ffmpeg and ffprobe")
+		val profile = Profile.currentProfile()
+		val contentPluginConfiguration = profile.getCustomSettings(ContentPluginMain.zoneConfigurationKey).asInstanceOf[ContentPlayerPluginConfiguration]
+
+		if (StringUtils.isNotEmpty(contentPluginConfiguration.ffmpegExecutable) && StringUtils.isNotEmpty(contentPluginConfiguration.ffprobeExecutable)) {
+			ffmpeg = new FFmpeg(contentPluginConfiguration.ffmpegExecutable)
+			ffprobe = new FFprobe(contentPluginConfiguration.ffprobeExecutable)
+		}
+	}
+
+	def checkInitialization(): Boolean = {
+		if (ffprobe == null || ffmpeg == null) {
+			initialize()
+
+			return ffprobe != null && ffmpeg != null
+		}
+		true
+	}
+
+	def getResolution(path: Path): Pair[Int, Int] = {
+		if (!checkInitialization()) {
+			return null
+		}
+
+		val probeResult = ffprobe.probe(path.toAbsolutePath.toString)
+
+		val stream = probeResult.streams.head
+		if (stream != null) {
+			return new Pair(stream.width, stream.height)
+		}
+		null
+	}
+
+	def convertMediaVStack(path: Path): Unit = {
+		if (!checkInitialization()) {
+			return
+		}
+
+		val globalSettings = PlayPadPlugin.getInstance.getGlobalSettings
+		val convertPath = globalSettings.getCachePath.resolve(path.getFileName + ".mp4")
+
+		if (Files.notExists(convertPath.getParent)) {
+			Files.createDirectories(convertPath.getParent)
+		}
+
+		val builder = new FFmpegBuilder()
+		  .addInput(path.toAbsolutePath.toString)
+		  .addInput(path.toAbsolutePath.toString)
+		  .overrideOutputFiles(true)
+		  .addOutput(convertPath.toAbsolutePath.toString)
+		  .setVideoBitRate(5_000_000)
+		  .setVideoCodec("libx264")
+		  .addExtraArgs("-filter_complex", "vstack")
+		  .done()
+
+		val executor = new FFmpegExecutor(ffmpeg, ffprobe)
+		executor.createJob(builder).run()
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala
index 22836f6b9326accde9478b56a7e65861ff3e5fb3..14526479256ffb973bc8fd278df46f82b2b82f32 100644
--- a/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala
+++ b/PlayWallPlugins/PlayWallPluginContentPlayer/src/main/scala/de/tobias/playpad/plugin/content/util/package.scala
@@ -2,6 +2,8 @@ package de.tobias.playpad.plugin.content
 
 import javafx.collections.ObservableList
 
+import java.util
+
 package object util {
 
 	implicit class ObservableListExtension[E >: Null](list: ObservableList[E]) {
@@ -15,6 +17,31 @@ package object util {
 
 		def apply(index: Int): E = list.get(index)
 
+		def length: Int = list.size()
+
+		def isNotEmpty: Boolean = !list.isEmpty
+
+		def indexWhere(predicate: E => Boolean): Int = {
+			for (i <- 0 until list.size()) {
+				if (predicate(list.get(i))) {
+					return i
+				}
+			}
+			-1
+		}
+	}
+
+	implicit class ListExtension[E >: Null](list: util.List[E]) {
+		def head: E = {
+			if (list.isEmpty) {
+				return null
+			}
+
+			list.get(0)
+		}
+
+		def apply(index: Int): E = list.get(index)
+
 		def length: Long = list.size()
 
 		def isNotEmpty: Boolean = !list.isEmpty
diff --git a/PlayWallPlugins/PlayWallPluginEqualizer/pom.xml b/PlayWallPlugins/PlayWallPluginEqualizer/pom.xml
index 2e1be5a7b2293b617048c7d311c779003afabad2..8097ef2cead6f6da5ba2eac0b15064decef70653 100644
--- a/PlayWallPlugins/PlayWallPluginEqualizer/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginEqualizer/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallPluginEqualizer</artifactId>
diff --git a/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java/de/tobias/playpad/equalizerplugin/impl/EqualizerPluginImpl.java b/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java/de/tobias/playpad/equalizerplugin/impl/EqualizerPluginImpl.java
index f65d2d28af1b7df25ed6f40c6a92f36a323deb95..5561cb969e7e61fd3182e04268d8ffaf21632bd4 100644
--- a/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java/de/tobias/playpad/equalizerplugin/impl/EqualizerPluginImpl.java
+++ b/PlayWallPlugins/PlayWallPluginEqualizer/src/main/java/de/tobias/playpad/equalizerplugin/impl/EqualizerPluginImpl.java
@@ -75,7 +75,12 @@ public class EqualizerPluginImpl implements PlayPadPluginStub, PluginArtifact, M
 	}
 
 	@Override
-	public void onStatusChange(Pad pad, PadStatus newValue) {
+	public void onNameChanged(Pad pad, String oldValue, String newValue) {
+		// Nothing to implement
+	}
+
+	@Override
+	public void onStatusChange(Pad pad, PadStatus oldValue, PadStatus newValue) {
 		if (newValue == PadStatus.PLAY) {
 			onPlay(pad);
 		} else if (newValue == PadStatus.STOP) {
diff --git a/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources/plugin.yml
index c14ec70cc9806a4c1a0ec0ab38009bded4fd93ac..e38303219263f8b911b9b2a20654b58ff118d9f9 100644
--- a/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginEqualizer/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "EqualizerPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 6
+build: 7
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/pom.xml b/PlayWallPlugins/PlayWallPluginLaunchpad/pom.xml
index 796dc9ad10ed73ebf34dfecdd5c6b6f3e798b3cd..fd2be163795c167fb1e426c7e41b3d8972d62e9c 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallPluginLaunchpad</artifactId>
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/LaunchpadPluginImpl.java b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/LaunchpadPluginImpl.java
index 0a62f947c6504e42134e4e139aaff43886478acf..971674a1105103c029cf319691fe08f2c9a6331c 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/LaunchpadPluginImpl.java
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/LaunchpadPluginImpl.java
@@ -4,6 +4,7 @@ import de.thecodelabs.logger.Logger;
 import de.thecodelabs.midi.midi.feedback.MidiFeedbackTranscriptionRegistry;
 import de.thecodelabs.plugins.PluginDescriptor;
 import de.thecodelabs.plugins.versionizer.PluginArtifact;
+import de.thecodelabs.utils.util.Localization;
 import de.tobias.playpad.launchpadplugin.midi.mk2.LaunchPadMK2;
 import de.tobias.playpad.launchpadplugin.midi.s.LaunchPadS;
 import de.tobias.playpad.plugin.Module;
@@ -17,6 +18,7 @@ public class LaunchpadPluginImpl implements PlayPadPluginStub, PluginArtifact {
 	@Override
 	public void startup(PluginDescriptor descriptor) {
 		module = new Module(descriptor.getName(), descriptor.getArtifactId());
+		Localization.addResourceBundle("lang/l10n", LaunchpadPluginImpl.class.getClassLoader());
 
 		final MidiFeedbackTranscriptionRegistry registry = MidiFeedbackTranscriptionRegistry.getInstance();
 		registry.register(LaunchPadMK2.NAME, new LaunchPadMK2());
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/MapParser.java b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/MapParser.java
index 6870092286dc3456b5a0991a3f032792ef845e76..76ccdfc38c10ee139c82c4e3d8c4e9fec50bc4ee 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/MapParser.java
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/impl/MapParser.java
@@ -1,5 +1,6 @@
 package de.tobias.playpad.launchpadplugin.impl;
 
+import de.thecodelabs.midi.feedback.FeedbackColor;
 import de.thecodelabs.utils.io.IOUtils;
 
 import java.io.IOException;
@@ -12,8 +13,8 @@ public class MapParser {
 	private MapParser() {
 	}
 
-	public static Map<String, String> load(URL resource) throws IOException {
-		Map<String, String> items = new HashMap<>();
+	public static Map<String, FeedbackColor> load(URL resource, Class<? extends Enum> type) throws IOException {
+		Map<String, FeedbackColor> items = new HashMap<>();
 		for (String line : IOUtils.readURL(resource).split("\n")) {
 			line = line.trim();
 
@@ -25,8 +26,8 @@ public class MapParser {
 			String[] split = line.split("=");
 			if (split.length == 2) {
 				String color = split[0];
-				String val = split[1];
-				items.put(color, val);
+				Enum<?> val = Enum.valueOf(type, split[1]);
+				items.put(color, (FeedbackColor) val);
 			}
 		}
 		return items;
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2.java b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2.java
index bd6f1c60c5d2bf91a7d71ee5fcf51429fdf258f8..aabc7fb7d6f8c3a766f6ea462c992eeb640fab78 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2.java
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2.java
@@ -11,14 +11,12 @@ import de.thecodelabs.midi.midi.MidiCommand;
 import de.thecodelabs.midi.midi.MidiCommandType;
 import de.thecodelabs.midi.midi.feedback.MidiFeedbackTranscript;
 import de.tobias.playpad.action.feedback.FeedbackColorSuggester;
-import de.tobias.playpad.action.feedback.LightMode;
 import de.tobias.playpad.launchpadplugin.impl.MapParser;
 import de.tobias.playpad.profile.Profile;
 import javafx.scene.paint.Color;
 
 import java.net.URL;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
 
 public class LaunchPadMK2 implements MidiFeedbackTranscript, FeedbackColorSuggester {
 
@@ -26,12 +24,22 @@ public class LaunchPadMK2 implements MidiFeedbackTranscript, FeedbackColorSugges
 	public static final String NATIVE_NAME = "CoreMIDI4J - Launchpad MK2";
 
 	// Modern Colors mapped to the colors of the launchpad
-	private static Map<String, String> mapProperties;
+	private static final String[] COLOR_MAPPING_FILES = {
+			"launchpad_mk2_colorful.map",
+			"launchpad_mk2_high.map",
+			"launchpad_mk2_normal.map",
+			"launchpad_mk2_low.map"
+	};
+	private static final String DEFAULT_COLOR_MAPPING = COLOR_MAPPING_FILES[0];
+	private static final Map<String, Map<String, FeedbackColor>> midiColorMappings;
 
 	static {
+		midiColorMappings = new HashMap<>();
 		try {
-			URL resource = LaunchPadMK2.class.getClassLoader().getResource("launchpad_mk2.map");
-			mapProperties = MapParser.load(resource);
+			for (String mappingFile : COLOR_MAPPING_FILES) {
+				URL resource = LaunchPadMK2.class.getClassLoader().getResource(mappingFile);
+				midiColorMappings.put(mappingFile, MapParser.load(resource, LaunchPadMK2Color.class));
+			}
 		} catch (Exception e) {
 			Logger.error(e);
 		}
@@ -86,7 +94,12 @@ public class LaunchPadMK2 implements MidiFeedbackTranscript, FeedbackColorSugges
 
 	@Override
 	public FeedbackValue[] getFeedbackValues() {
-		return LaunchPadMK2Color.values();
+		String midiColorMapping = Profile.currentProfile().getProfileSettings().getMidiColorMapping();
+		if (midiColorMapping == null || midiColorMapping.isEmpty()) {
+			midiColorMapping = DEFAULT_COLOR_MAPPING;
+		}
+		final Map<String, FeedbackColor> colorMap = midiColorMappings.get(midiColorMapping);
+		return colorMap.values().stream().sorted().distinct().toArray(FeedbackColor[]::new);
 	}
 
 	@Override
@@ -94,14 +107,23 @@ public class LaunchPadMK2 implements MidiFeedbackTranscript, FeedbackColorSugges
 		return Optional.ofNullable(LaunchPadMK2Color.valueOf(b));
 	}
 
+	/*
+	FeedbackColorSuggester
+	 */
+
+	@Override
+	public List<String> getMidiColorMappings() {
+		return Arrays.asList(COLOR_MAPPING_FILES);
+	}
+
 	@Override
 	public FeedbackColor suggest(Color color) {
-		if (mapProperties.containsKey(color.toString())) {
-			String nameOfConst = mapProperties.get(color.toString());
-			final LaunchPadMK2Color mk2Color = LaunchPadMK2Color.valueOf(nameOfConst);
+		final String midiColorMapping = Optional.ofNullable(Profile.currentProfile().getProfileSettings().getMidiColorMapping())
+				.orElse(DEFAULT_COLOR_MAPPING);
+		final Map<String, FeedbackColor> colorMap = midiColorMappings.get(midiColorMapping);
 
-			LightMode lightMode = Profile.currentProfile().getProfileSettings().getLightMode();
-			return mk2Color.translate(lightMode);
+		if (colorMap.containsKey(color.toString())) {
+			return colorMap.get(color.toString());
 		}
 		return null;
 	}
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2Color.java b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2Color.java
index a0d357257ae6fe9a7ec84e15eb65ad904d5471c9..fa9e780c913b4dfe72ca7c2ea1f01e416a226896 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2Color.java
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/java/de/tobias/playpad/launchpadplugin/midi/mk2/LaunchPadMK2Color.java
@@ -1,118 +1,105 @@
 package de.tobias.playpad.launchpadplugin.midi.mk2;
 
 import de.thecodelabs.midi.feedback.FeedbackColor;
-import de.tobias.playpad.action.feedback.LightMode;
 import javafx.scene.paint.Color;
 import javafx.scene.paint.Paint;
 
-public enum LaunchPadMK2Color implements FeedbackColor, LightMode.ILightMode {
+public enum LaunchPadMK2Color implements FeedbackColor {
+
+	/*
+	High
+	Normal
+	Low
+	 */
 
 	// White
-	C0_1(1, Color.rgb(255, 255, 255), LightMode.LOW),
-	C0_2(2, Color.rgb(255, 255, 255), LightMode.NORMAL),
-	C0_3(3, Color.rgb(255, 255, 255), LightMode.HIGH),
+	C0_3(3, Color.rgb(255, 255, 255)),
+	C0_2(2, Color.rgb(255, 255, 255).darker()),
+	C0_1(1, Color.rgb(255, 255, 255).darker().darker()),
 
 	// RED
-	C1_1(4, Color.rgb(255, 0, 0), LightMode.HIGH),
-	C1_2(5, Color.rgb(255, 0, 0), LightMode.NORMAL),
-	C1_3(6, Color.rgb(255, 0, 0), LightMode.MIDDLE),
-	C1_4(7, Color.rgb(255, 0, 0), LightMode.LOW),
+	C1_1(5, Color.rgb(255, 0, 0)),
+	C1_2(6, Color.rgb(255, 0, 0).darker()),
+	C1_3(7, Color.rgb(255, 0, 0).darker().darker()),
 
 	// Orange
-	C2_1(8, Color.rgb(255, 127, 0), LightMode.HIGH),
-	C2_2(9, Color.rgb(255, 127, 0), LightMode.NORMAL),
-	C2_3(10, Color.rgb(255, 127, 0), LightMode.MIDDLE),
-	C2_4(11, Color.rgb(255, 127, 0), LightMode.LOW),
+	C2_1(9, Color.rgb(255, 127, 0)),
+	C2_2(10, Color.rgb(255, 127, 0).darker()),
+	C2_3(11, Color.rgb(255, 127, 0).darker().darker()),
 
 	// LIME
-	C3_1(12, Color.rgb(235, 255, 39), LightMode.HIGH),
-	C3_2(13, Color.rgb(235, 255, 39), LightMode.NORMAL),
-	C3_3(14, Color.rgb(235, 255, 39), LightMode.MIDDLE),
-	C3_4(15, Color.rgb(235, 255, 39), LightMode.LOW),
+	C3_1(13, Color.rgb(235, 255, 39)),
+	C3_2(14, Color.rgb(235, 255, 39).darker()),
+	C3_3(15, Color.rgb(235, 255, 39).darker().darker()),
 
 	// LIGHT GREEN
-	C4_1(16, Color.rgb(123, 255, 66), LightMode.HIGH),
-	C4_2(17, Color.rgb(123, 255, 66), LightMode.NORMAL),
-	C4_3(18, Color.rgb(123, 255, 66), LightMode.MIDDLE),
-	C4_4(19, Color.rgb(123, 255, 66), LightMode.LOW),
+	C4_1(17, Color.rgb(123, 255, 66)),
+	C4_2(18, Color.rgb(123, 255, 66).darker()),
+	C4_3(19, Color.rgb(123, 255, 66).darker().darker()),
 
 	// GREEN
-	C5_1(20, Color.rgb(0, 255, 0), LightMode.HIGH),
-	C5_2(21, Color.rgb(0, 255, 0), LightMode.NORMAL),
-	C5_3(22, Color.rgb(0, 255, 0), LightMode.MIDDLE),
-	C5_4(23, Color.rgb(0, 255, 0), LightMode.LOW),
+	C5_1(21, Color.rgb(0, 255, 0)),
+	C5_2(22, Color.rgb(0, 255, 0).darker()),
+	C5_3(23, Color.rgb(0, 255, 0).darker().darker()),
 
 	// GREEN
-	C6_1(24, Color.rgb(62, 255, 112), LightMode.HIGH),
-	C6_2(25, Color.rgb(62, 255, 112), LightMode.NORMAL),
-	C6_3(26, Color.rgb(62, 255, 112), LightMode.MIDDLE),
-	C6_4(27, Color.rgb(62, 255, 112), LightMode.LOW),
+	C6_1(25, Color.rgb(62, 255, 112)),
+	C6_2(26, Color.rgb(62, 255, 112).darker()),
+	C6_3(27, Color.rgb(62, 255, 112).darker().darker()),
 
 	// TURKEY
-	C7_1(28, Color.rgb(62, 255, 112), LightMode.HIGH),
-	C7_2(29, Color.rgb(62, 255, 112), LightMode.NORMAL),
-	C7_3(30, Color.rgb(62, 255, 112), LightMode.MIDDLE),
-	C7_4(31, Color.rgb(62, 255, 112), LightMode.LOW),
+	C7_1(29, Color.rgb(62, 255, 112)),
+	C7_2(30, Color.rgb(62, 255, 112).darker()),
+	C7_3(31, Color.rgb(62, 255, 112).darker().darker()),
 
 	// TURKEY
-	C8_1(32, Color.rgb(101, 255, 196), LightMode.HIGH),
-	C8_2(33, Color.rgb(101, 255, 196), LightMode.NORMAL),
-	C8_3(34, Color.rgb(101, 255, 196), LightMode.MIDDLE),
-	C8_4(35, Color.rgb(101, 255, 196), LightMode.LOW),
+	C8_1(33, Color.rgb(101, 255, 196)),
+	C8_2(34, Color.rgb(101, 255, 196).darker()),
+	C8_3(35, Color.rgb(101, 255, 196).darker().darker()),
 
 	// LIGHT BLUE
-	C9_1(36, Color.rgb(91, 255, 253), LightMode.HIGH),
-	C9_2(37, Color.rgb(91, 255, 253), LightMode.NORMAL),
-	C9_3(38, Color.rgb(91, 255, 253), LightMode.MIDDLE),
-	C9_4(39, Color.rgb(91, 255, 253), LightMode.LOW),
+	C9_1(37, Color.rgb(91, 255, 253)),
+	C9_2(38, Color.rgb(91, 255, 253).darker()),
+	C9_3(39, Color.rgb(91, 255, 253).darker().darker()),
 
 	// BLUE
-	C10_1(40, Color.rgb(69, 169, 255), LightMode.HIGH),
-	C10_2(41, Color.rgb(69, 169, 255), LightMode.NORMAL),
-	C10_3(42, Color.rgb(69, 169, 255), LightMode.MIDDLE),
-	C10_4(43, Color.rgb(69, 169, 255), LightMode.LOW),
+	C10_1(41, Color.rgb(69, 169, 255)),
+	C10_2(42, Color.rgb(69, 169, 255).darker()),
+	C10_3(43, Color.rgb(69, 169, 255).darker().darker()),
 
 	// DARK BLUE
-	C11_1(44, Color.rgb(30, 67, 255), LightMode.HIGH),
-	C11_2(45, Color.rgb(30, 67, 255), LightMode.NORMAL),
-	C11_3(46, Color.rgb(30, 67, 255), LightMode.MIDDLE),
-	C11_4(47, Color.rgb(30, 67, 255), LightMode.LOW),
+	C11_1(45, Color.rgb(30, 67, 255)),
+	C11_2(46, Color.rgb(30, 67, 255).darker()),
+	C11_3(47, Color.rgb(30, 67, 255).darker().darker()),
 
 	// PURPLE
-	C12_1(48, Color.rgb(125, 73, 255), LightMode.HIGH),
-	C12_2(49, Color.rgb(125, 73, 255), LightMode.NORMAL),
-	C12_3(50, Color.rgb(125, 73, 255), LightMode.MIDDLE),
-	C12_4(51, Color.rgb(125, 73, 255), LightMode.LOW),
+	C12_1(49, Color.rgb(125, 73, 255)),
+	C12_2(50, Color.rgb(125, 73, 255).darker()),
+	C12_3(51, Color.rgb(125, 73, 255).darker().darker()),
 
 	// VIOLET
-	C13_1(52, Color.rgb(254, 85, 255), LightMode.HIGH),
-	C13_2(53, Color.rgb(254, 85, 255), LightMode.NORMAL),
-	C13_3(54, Color.rgb(254, 85, 255), LightMode.MIDDLE),
-	C13_4(55, Color.rgb(254, 85, 255), LightMode.LOW),
+	C13_1(53, Color.rgb(254, 85, 255)),
+	C13_2(54, Color.rgb(254, 85, 255).darker()),
+	C13_3(55, Color.rgb(254, 85, 255).darker().darker()),
 
 	// VIOLET
-	C14_1(56, Color.rgb(255, 75, 191), LightMode.HIGH),
-	C14_2(57, Color.rgb(255, 75, 191), LightMode.NORMAL),
-	C14_3(58, Color.rgb(255, 75, 191), LightMode.MIDDLE),
-	C14_4(59, Color.rgb(255, 75, 191), LightMode.LOW),
+	C14_1(57, Color.rgb(255, 75, 191)),
+	C14_2(58, Color.rgb(255, 75, 191).darker()),
+	C14_3(59, Color.rgb(255, 75, 191).darker().darker()),
 
 	// BROWN
-	C15_1(60, Color.rgb(255, 100, 69), LightMode.HIGH),
-	C15_2(61, Color.rgb(255, 100, 69), LightMode.NORMAL),
-	C15_3(62, Color.rgb(255, 100, 69), LightMode.MIDDLE),
-	C15_4(63, Color.rgb(255, 100, 69), LightMode.LOW);
+	C15_1(61, Color.rgb(255, 100, 69)),
+	C15_2(62, Color.rgb(255, 100, 69).darker()),
+	C15_3(63, Color.rgb(255, 100, 69).darker().darker());
 
-	private int midi;
-	private Color color;
-	private LightMode lightMode;
+	private final int midi;
+	private final Color color;
 
-	LaunchPadMK2Color(int midi, Color color, LightMode lightMode) {
+	LaunchPadMK2Color(int midi, Color color) {
 		this.midi = midi;
 		this.color = color;
-		this.lightMode = lightMode;
 	}
 
-
 	@Override
 	public Paint getColor() {
 		return color;
@@ -123,24 +110,6 @@ public enum LaunchPadMK2Color implements FeedbackColor, LightMode.ILightMode {
 		return (byte) midi;
 	}
 
-	@Override
-	public LightMode getLightMode() {
-		return lightMode;
-	}
-
-	@Override
-	public FeedbackColor translate(LightMode lightMode) {
-		for (LaunchPadMK2Color instance : values()) {
-			if (instance.getColor().equals(this.getColor())) {
-				if (instance.lightMode == lightMode) {
-					return instance;
-				}
-			}
-		}
-		return null;
-	}
-
-
 	public static FeedbackColor valueOf(int id) {
 		for (LaunchPadMK2Color color : values()) {
 			if (color.getValue() == id) {
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/lang/l10n.properties b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/lang/l10n.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c0cbfcf89b21f43273678ef74426a9257d53e83e
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/lang/l10n.properties
@@ -0,0 +1,4 @@
+MidiColorMapping.launchpad_mk2_colorful.map=Farbenfroh
+MidiColorMapping.launchpad_mk2_high.map=Hell
+MidiColorMapping.launchpad_mk2_normal.map=Normal
+MidiColorMapping.launchpad_mk2_low.map=Dunkel
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_colorful.map b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_colorful.map
new file mode 100644
index 0000000000000000000000000000000000000000..e7c0ea99f1f837cfe84282fcbb9c707d5d4d64cc
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_colorful.map
@@ -0,0 +1,57 @@
+% RED
+0xef9a9aff=C1_3
+0xef5350ff=C1_2
+0xe53935ff=C1_1
+
+% DARK_RED
+0xd92349ff=C1_3
+0xc92349ff=C1_2
+0xa90329ff=C1_1
+
+% PINK
+0xf48fb1ff=C14_3
+0xec407aff=C14_2
+0xd81b60ff=C14_1
+
+% PURPLE
+0xce93d8ff=C12_3
+0xab47bcff=C12_2
+0x8e24aaff=C12_1
+
+% LIGHT_BLUE
+0x80deeaff=C10_3
+0x26c6daff=C10_2
+0x00acc1ff=C10_1
+
+% BLUE
+0x90caf9ff=C11_3
+0x42a5f5ff=C11_2
+0x1e88e5ff=C11_1
+
+% LIGHT_GREEN
+0xc5e1a5ff=C5_3
+0x9ccc65ff=C5_2
+0x7cb342ff=C5_1
+
+% LIME
+0xe6ee9cff=C4_3
+0xd4e157ff=C4_2
+0xc0ca33ff=C4_1
+
+% YELLOW
+0xfff59dff=C3_3
+0xffee58ff=C3_2
+0xfdd835ff=C3_1
+
+% ORANGE
+0xffcc80ff=C2_3
+0xffa726ff=C2_2
+0xfb8c00ff=C2_1
+
+% GRAY
+0xeeeeeeff=C0_3
+0xccccccff=C0_3
+0xaaaaaaff=C0_3
+0x888888ff=C0_2
+0x666666ff=C0_2
+0x444444ff=C0_1
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_high.map b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_high.map
new file mode 100644
index 0000000000000000000000000000000000000000..4f6532323fd9c3767adaa9936c7b28dd4d5c4cd6
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_high.map
@@ -0,0 +1,57 @@
+% RED
+0xef9a9aff=C1_1
+0xef5350ff=C1_1
+0xe53935ff=C1_1
+
+% DARK_RED
+0xd92349ff=C1_1
+0xc92349ff=C1_1
+0xa90329ff=C1_1
+
+% PINK
+0xf48fb1ff=C14_1
+0xec407aff=C14_1
+0xd81b60ff=C14_1
+
+% PURPLE
+0xce93d8ff=C12_1
+0xab47bcff=C12_1
+0x8e24aaff=C12_1
+
+% LIGHT_BLUE
+0x80deeaff=C10_1
+0x26c6daff=C10_1
+0x00acc1ff=C10_1
+
+% BLUE
+0x90caf9ff=C11_1
+0x42a5f5ff=C11_1
+0x1e88e5ff=C11_1
+
+% LIGHT_GREEN
+0xc5e1a5ff=C5_1
+0x9ccc65ff=C5_1
+0x7cb342ff=C5_1
+
+% LIME
+0xe6ee9cff=C4_1
+0xd4e157ff=C4_1
+0xc0ca33ff=C4_1
+
+% YELLOW
+0xfff59dff=C3_1
+0xffee58ff=C3_1
+0xfdd835ff=C3_1
+
+% ORANGE
+0xffcc80ff=C2_1
+0xffa726ff=C2_1
+0xfb8c00ff=C2_1
+
+% GRAY
+0xeeeeeeff=C0_3
+0xccccccff=C0_3
+0xaaaaaaff=C0_3
+0x888888ff=C0_2
+0x666666ff=C0_2
+0x444444ff=C0_1
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_low.map b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_low.map
new file mode 100644
index 0000000000000000000000000000000000000000..34053852f3be518718bf5f982b255aa695b131b6
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_low.map
@@ -0,0 +1,57 @@
+% RED
+0xef9a9aff=C1_3
+0xef5350ff=C1_3
+0xe53935ff=C1_3
+
+% DARK_RED
+0xd92349ff=C1_3
+0xc92349ff=C1_3
+0xa90329ff=C1_3
+
+% PINK
+0xf48fb1ff=C14_3
+0xec407aff=C14_3
+0xd81b60ff=C14_3
+
+% PURPLE
+0xce93d8ff=C12_3
+0xab47bcff=C12_3
+0x8e24aaff=C12_3
+
+% LIGHT_BLUE
+0x80deeaff=C10_3
+0x26c6daff=C10_3
+0x00acc1ff=C10_3
+
+% BLUE
+0x90caf9ff=C11_3
+0x42a5f5ff=C11_3
+0x1e88e5ff=C11_3
+
+% LIGHT_GREEN
+0xc5e1a5ff=C5_3
+0x9ccc65ff=C5_3
+0x7cb342ff=C5_3
+
+% LIME
+0xe6ee9cff=C4_3
+0xd4e157ff=C4_3
+0xc0ca33ff=C4_3
+
+% YELLOW
+0xfff59dff=C3_3
+0xffee58ff=C3_3
+0xfdd835ff=C3_3
+
+% ORANGE
+0xffcc80ff=C2_3
+0xffa726ff=C2_3
+0xfb8c00ff=C2_3
+
+% GRAY
+0xeeeeeeff=C0_3
+0xccccccff=C0_3
+0xaaaaaaff=C0_3
+0x888888ff=C0_2
+0x666666ff=C0_2
+0x444444ff=C0_1
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2.map b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_normal.map
similarity index 97%
rename from PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2.map
rename to PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_normal.map
index 72565457824aa9a5741c5a28924031efef3ea9db..8ab039471cdd4999aa4a59cd591c75b5138221d9 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2.map
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/launchpad_mk2_normal.map
@@ -54,4 +54,4 @@
 0xaaaaaaff=C0_3
 0x888888ff=C0_2
 0x666666ff=C0_2
-0x444444ff=C0_2
\ No newline at end of file
+0x444444ff=C0_1
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/plugin.yml
index 61c6c0118e1b2e99255205be0cca94044cfc8c64..79d46638339528325b57baf3609a0ccddb703a01 100644
--- a/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginLaunchpad/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "LaunchpadPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 5
+build: 6
diff --git a/PlayWallPlugins/PlayWallPluginMedia/pom.xml b/PlayWallPlugins/PlayWallPluginMedia/pom.xml
index f420120ac351928558988fd3c347cec1302a364b..0acf49680487153afc8cd77b3c4cca2d1844cfeb 100644
--- a/PlayWallPlugins/PlayWallPluginMedia/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginMedia/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
 
     <artifactId>PlayWallPluginMedia</artifactId>
diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImageContent.java b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImageContent.java
index 02a9b5c4b719d5de1b2d6df56893f91cb701df54..7f7a3c6788ee645db2518bb702bdb0ed61bce380 100644
--- a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImageContent.java
+++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/image/ImageContent.java
@@ -30,7 +30,7 @@ public class ImageContent extends PadContent {
 	}
 
 	@Override
-	public void play() {
+	public void play(boolean withFadeIn) {
 		Path mediaPath = getPad().getPath();
 		MediaPluginImpl.getInstance().getVideoViewController().setImage(mediaPath.toUri().toString(), getPad());
 	}
diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoContent.java b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoContent.java
index ba1af9b930e6d324e0d6e30b53644a8cb358aa5d..ec0ccea865bad0405ad84637fcdd0b4622a4d3a3 100644
--- a/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoContent.java
+++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/java/de/tobias/playpad/plugin/media/video/VideoContent.java
@@ -57,7 +57,7 @@ public class VideoContent extends PadContent implements Pauseable, Durationable,
 	}
 
 	@Override
-	public void play() {
+	public void play(boolean withFadeIn) {
 		getPad().setEof(false);
 		MediaPluginImpl.getInstance().getVideoViewController().setMediaPlayer(player, getPad());
 		if (holdLastFrame) {
diff --git a/PlayWallPlugins/PlayWallPluginMedia/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginMedia/src/main/resources/plugin.yml
index c664750bebf2009d4d47309241bd51a3c1d93461..8a072ecb5ae2a1cb084761fee1d34c21f779c44f 100644
--- a/PlayWallPlugins/PlayWallPluginMedia/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginMedia/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "MediaPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 9
+build: 10
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.dll b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.dll
index bac58da0e22e4913990f86819a4043a21ed58fbd..681575270f41d99b3d9e75e025344d9eed5b6fdc 100755
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.dll and b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.dll b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.dll
index 97fa3d253f0980c173c27bb0c1431bd0092a5bdd..c4193f420d4a1b554c9db5cef5bd85e78f2dc025 100755
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.dll and b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.jar b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.jar
index 84f92d5c6577cfbd44199ab0f36d18b7afe6b5a1..6ef18f637600a47a2b2c073133e94727362d65b6 100755
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.jar and b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.j4n.jar differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.proxygen.xml b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.proxygen.xml
index a397d0d470c1c32fd9c80e618aac85e2c2716d17..329aeb9fd659b13fb17e94f8fda1c106d0724b26 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.proxygen.xml
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/NativeAudio.proxygen.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<jni4net-proxygen xmlns="http://jni4net.sf.net/0.8.8.0/toolConfig.xsd">
-    <TargetDirJvm>.\jvm</TargetDirJvm>
-    <TargetDirClr>.\clr</TargetDirClr>
-    <AssemblyReference Assembly="NativeAudio.dll" Generate="true"/>
+<jni4net-proxygen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jni4net.sf.net/0.8.8.0/toolConfig.xsd">
+  <TargetDirJvm>.\jvm</TargetDirJvm>
+  <TargetDirClr>.\clr</TargetDirClr>
+  <AssemblyReference Assembly="NativeAudio.dll" Generate="true" />
 </jni4net-proxygen>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/build.cmd b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/build.cmd
index adc153ff6e679a35129da61f434824f460ad6daf..6f16f61725c41b781089c911bd2ab18ef3215b06 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/build.cmd
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/build.cmd
@@ -4,17 +4,17 @@ if not exist target\classes mkdir target\classes
 
 
 echo compile classes
-javac -nowarn -d target\classes -sourcepath jvm -cp "d:\programmieren\git-java\playwall\playwallnativewin\j4n\jni4net.j-0.8.8.0.jar"; "jvm\nativeaudio\LoopStream.java" "jvm\nativeaudio\NativeAudio.java" 
+javac -nowarn -d target\classes -sourcepath jvm -cp "c:\users\tobias\ideaprojects\playwalldesktop\playwallplugins\playwallpluginnativeaudio\j4n\jni4net.j-0.8.8.0.jar"; "jvm\nativeaudio\NativeAudio.java" 
 IF %ERRORLEVEL% NEQ 0 goto end
 
 
 echo NativeAudio.j4n.jar 
-jar cvf NativeAudio.j4n.jar  -C target\classes "nativeaudio\LoopStream.class"  -C target\classes "nativeaudio\NativeAudio.class"  > nul 
+jar cvf NativeAudio.j4n.jar  -C target\classes "nativeaudio\NativeAudio.class"  > nul 
 IF %ERRORLEVEL% NEQ 0 goto end
 
 
 echo NativeAudio.j4n.dll 
-csc /nologo /warn:0 /t:library /out:NativeAudio.j4n.dll /recurse:clr\*.cs  /reference:"D:\Programmieren\Git-Java\PlayWall\PlayWallNativeWin\j4n\NativeAudio.dll" /reference:"D:\Programmieren\Git-Java\PlayWall\PlayWallNativeWin\j4n\NAudio.dll" /reference:"D:\Programmieren\Git-Java\PlayWall\PlayWallNativeWin\j4n\jni4net.n-0.8.8.0.dll"
+csc /nologo /warn:0 /t:library /out:NativeAudio.j4n.dll /recurse:clr\*.cs  /reference:"C:\Users\tobias\IdeaProjects\PlayWallDesktop\PlayWallPlugins\PlayWallPluginNativeAudio\j4n\NativeAudio.dll" /reference:"C:\Users\tobias\IdeaProjects\PlayWallDesktop\PlayWallPlugins\PlayWallPluginNativeAudio\j4n\jni4net.n-0.8.8.0.dll"
 IF %ERRORLEVEL% NEQ 0 goto end
 
 
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/LoopStream.generated.cs b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/LoopStream.generated.cs
deleted file mode 100644
index f2956bbb4c618bf2d1e6b62e0e85293dbd7c4bc6..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/LoopStream.generated.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     This code was generated by jni4net. See http://jni4net.sourceforge.net/ 
-//     Runtime Version:4.0.30319.42000
-//
-//     Changes to this file may cause incorrect behavior and will be lost if
-//     the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace NativeAudio {
-    
-    
-    #region Component Designer generated code 
-    public partial class LoopStream_ {
-        
-        public static global::java.lang.Class _class {
-            get {
-                return global::NativeAudio.@__LoopStream.staticClass;
-            }
-        }
-    }
-    #endregion
-    
-    #region Component Designer generated code 
-    [global::net.sf.jni4net.attributes.JavaProxyAttribute(typeof(global::NativeAudio.LoopStream), typeof(global::NativeAudio.LoopStream_))]
-    [global::net.sf.jni4net.attributes.ClrWrapperAttribute(typeof(global::NativeAudio.LoopStream), typeof(global::NativeAudio.LoopStream_))]
-    internal sealed partial class @__LoopStream : global::java.lang.Object {
-        
-        internal new static global::java.lang.Class staticClass;
-        
-        private @__LoopStream(global::net.sf.jni4net.jni.JNIEnv @__env) : 
-                base(@__env) {
-        }
-        
-        private static void InitJNI(global::net.sf.jni4net.jni.JNIEnv @__env, java.lang.Class @__class) {
-            global::NativeAudio.@__LoopStream.staticClass = @__class;
-        }
-        
-        private static global::System.Collections.Generic.List<global::net.sf.jni4net.jni.JNINativeMethod> @__Init(global::net.sf.jni4net.jni.JNIEnv @__env, global::java.lang.Class @__class) {
-            global::System.Type @__type = typeof(__LoopStream);
-            global::System.Collections.Generic.List<global::net.sf.jni4net.jni.JNINativeMethod> methods = new global::System.Collections.Generic.List<global::net.sf.jni4net.jni.JNINativeMethod>();
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "getEnableLooping", "EnableLooping0", "()Z"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setEnableLooping", "EnableLooping1", "(Z)V"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "__ctorLoopStream0", "__ctorLoopStream0", "(Lnet/sf/jni4net/inj/IClrProxy;Lsystem/io/Stream;)V"));
-            return methods;
-        }
-        
-        private static bool EnableLooping0(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj) {
-            // ()Z
-            // ()Z
-            global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
-            bool @__return = default(bool);
-            try {
-            global::NativeAudio.LoopStream @__real = global::net.sf.jni4net.utils.Convertor.StrongJp2C<global::NativeAudio.LoopStream>(@__env, @__obj);
-            @__return = ((bool)(@__real.EnableLooping));
-            }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
-            return @__return;
-        }
-        
-        private static void EnableLooping1(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, bool value) {
-            // (Z)V
-            // (Z)V
-            global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
-            try {
-            global::NativeAudio.LoopStream @__real = global::net.sf.jni4net.utils.Convertor.StrongJp2C<global::NativeAudio.LoopStream>(@__env, @__obj);
-            @__real.EnableLooping = value;
-            }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
-        }
-        
-        private static void @__ctorLoopStream0(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__class, global::net.sf.jni4net.utils.JniLocalHandle @__obj, global::net.sf.jni4net.utils.JniLocalHandle sourceStream) {
-            // (Lsystem/io/Stream;)V
-            // (LNAudio/Wave/WaveStream;)V
-            global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
-            try {
-            global::NativeAudio.LoopStream @__real = new global::NativeAudio.LoopStream(global::net.sf.jni4net.utils.Convertor.StrongJp2C<global::NAudio.Wave.WaveStream>(@__env, sourceStream));
-            global::net.sf.jni4net.utils.Convertor.InitProxy(@__env, @__obj, @__real);
-            }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
-        }
-        
-        new internal sealed class ContructionHelper : global::net.sf.jni4net.utils.IConstructionHelper {
-            
-            public global::net.sf.jni4net.jni.IJvmProxy CreateProxy(global::net.sf.jni4net.jni.JNIEnv @__env) {
-                return new global::NativeAudio.@__LoopStream(@__env);
-            }
-        }
-    }
-    #endregion
-}
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/NativeAudio.generated.cs b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/NativeAudio.generated.cs
index 1edc5ae5b06a6890f39c438dac28f18db7b4883e..746adaff6124048fadc8714aea5c51c541d893f9 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/NativeAudio.generated.cs
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/clr/nativeaudio/NativeAudio.generated.cs
@@ -47,11 +47,12 @@ namespace NativeAudio {
             methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "getDuration", "getDuration4", "()D"));
             methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "getPosition", "getPosition5", "()D"));
             methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "isPlaying", "isPlaying6", "()Z"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setVolume", "setVolume7", "(F)V"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setLoop", "setLoop8", "(Z)V"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setDevice", "setDevice9", "(Ljava/lang/String;)V"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "getDevices", "getDevices10", "()[Ljava/lang/String;"));
-            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "unload", "unload11", "()V"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "seek", "seek7", "(J)V"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setVolume", "setVolume8", "(F)V"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setLoop", "setLoop9", "(Z)V"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "setDevice", "setDevice10", "(Ljava/lang/String;)V"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "getDevices", "getDevices11", "()[Ljava/lang/String;"));
+            methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "unload", "unload12", "()V"));
             methods.Add(global::net.sf.jni4net.jni.JNINativeMethod.Create(@__type, "__ctorNativeAudio0", "__ctorNativeAudio0", "(Lnet/sf/jni4net/inj/IClrProxy;)V"));
             return methods;
         }
@@ -134,7 +135,17 @@ namespace NativeAudio {
             return @__return;
         }
         
-        private static void setVolume7(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, float volume) {
+        private static void seek7(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, long duration) {
+            // (J)V
+            // (J)V
+            global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
+            try {
+            global::NativeAudio.NativeAudio @__real = global::net.sf.jni4net.utils.Convertor.StrongJp2C<global::NativeAudio.NativeAudio>(@__env, @__obj);
+            @__real.seek(duration);
+            }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
+        }
+        
+        private static void setVolume8(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, float volume) {
             // (F)V
             // (F)V
             global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
@@ -144,7 +155,7 @@ namespace NativeAudio {
             }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
         }
         
-        private static void setLoop8(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, bool loop) {
+        private static void setLoop9(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, bool loop) {
             // (Z)V
             // (Z)V
             global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
@@ -154,7 +165,7 @@ namespace NativeAudio {
             }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
         }
         
-        private static void setDevice9(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, global::net.sf.jni4net.utils.JniLocalHandle name) {
+        private static void setDevice10(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj, global::net.sf.jni4net.utils.JniLocalHandle name) {
             // (Ljava/lang/String;)V
             // (LSystem/String;)V
             global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
@@ -164,7 +175,7 @@ namespace NativeAudio {
             }catch (global::System.Exception __ex){@__env.ThrowExisting(__ex);}
         }
         
-        private static global::net.sf.jni4net.utils.JniHandle getDevices10(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__class) {
+        private static global::net.sf.jni4net.utils.JniHandle getDevices11(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__class) {
             // ()[Ljava/lang/String;
             // ()[LSystem/String;
             global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
@@ -175,7 +186,7 @@ namespace NativeAudio {
             return @__return;
         }
         
-        private static void unload11(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj) {
+        private static void unload12(global::System.IntPtr @__envp, global::net.sf.jni4net.utils.JniLocalHandle @__obj) {
             // ()V
             // ()V
             global::net.sf.jni4net.jni.JNIEnv @__env = global::net.sf.jni4net.jni.JNIEnv.Wrap(@__envp);
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/LoopStream.java b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/LoopStream.java
deleted file mode 100644
index 4fbc6a8b74ece4f1c2ec0fa5f46cfbc227219c5f..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/LoopStream.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// ------------------------------------------------------------------------------
-//  <autogenerated>
-//      This code was generated by jni4net. See http://jni4net.sourceforge.net/ 
-// 
-//      Changes to this file may cause incorrect behavior and will be lost if 
-//      the code is regenerated.
-//  </autogenerated>
-// ------------------------------------------------------------------------------
-
-package nativeaudio;
-
-@net.sf.jni4net.attributes.ClrType
-public class LoopStream extends system.io.Stream {
-
-	//<generated-proxy>
-	private static system.Type staticType;
-
-	protected LoopStream(net.sf.jni4net.inj.INJEnv __env, long __handle) {
-		super(__env, __handle);
-	}
-
-	@net.sf.jni4net.attributes.ClrConstructor("(LNAudio/Wave/WaveStream;)V")
-	public LoopStream(system.io.Stream sourceStream) {
-		super(((net.sf.jni4net.inj.INJEnv) (null)), 0);
-		nativeaudio.LoopStream.__ctorLoopStream0(this, sourceStream);
-	}
-
-	@net.sf.jni4net.attributes.ClrMethod("(Lsystem/io/Stream;)V")
-	private native static void __ctorLoopStream0(net.sf.jni4net.inj.IClrProxy thiz, system.io.Stream sourceStream);
-
-	@net.sf.jni4net.attributes.ClrMethod("()Z")
-	public native boolean getEnableLooping();
-
-	@net.sf.jni4net.attributes.ClrMethod("(Z)V")
-	public native void setEnableLooping(boolean value);
-
-	public static system.Type typeof() {
-		return nativeaudio.LoopStream.staticType;
-	}
-
-	private static void InitJNI(net.sf.jni4net.inj.INJEnv env, system.Type staticType) {
-		nativeaudio.LoopStream.staticType = staticType;
-	}
-	//</generated-proxy>
-}
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/NativeAudio.java b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/NativeAudio.java
index c11bf75943c816bcd0f2f1acf801478d74fb7790..d6673f2c4f0309a8428454ad5426a1c98396520f 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/NativeAudio.java
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/j4n/jvm/nativeaudio/NativeAudio.java
@@ -11,65 +11,68 @@ package nativeaudio;
 
 @net.sf.jni4net.attributes.ClrType
 public class NativeAudio extends system.Object {
-
-	//<generated-proxy>
-	private static system.Type staticType;
-
-	protected NativeAudio(net.sf.jni4net.inj.INJEnv __env, long __handle) {
-		super(__env, __handle);
-	}
-
-	@net.sf.jni4net.attributes.ClrConstructor("()V")
-	public NativeAudio() {
-		super(((net.sf.jni4net.inj.INJEnv) (null)), 0);
-		nativeaudio.NativeAudio.__ctorNativeAudio0(this);
-	}
-
-	@net.sf.jni4net.attributes.ClrMethod("()V")
-	private native static void __ctorNativeAudio0(net.sf.jni4net.inj.IClrProxy thiz);
-
-	@net.sf.jni4net.attributes.ClrMethod("(LSystem/String;)Z")
-	public native boolean load(java.lang.String path);
-
-	@net.sf.jni4net.attributes.ClrMethod("()V")
-	public native void play();
-
-	@net.sf.jni4net.attributes.ClrMethod("()V")
-	public native void pause();
-
-	@net.sf.jni4net.attributes.ClrMethod("()V")
-	public native void stop();
-
-	@net.sf.jni4net.attributes.ClrMethod("()D")
-	public native double getDuration();
-
-	@net.sf.jni4net.attributes.ClrMethod("()D")
-	public native double getPosition();
-
-	@net.sf.jni4net.attributes.ClrMethod("()Z")
-	public native boolean isPlaying();
-
-	@net.sf.jni4net.attributes.ClrMethod("(F)V")
-	public native void setVolume(float volume);
-
-	@net.sf.jni4net.attributes.ClrMethod("(Z)V")
-	public native void setLoop(boolean loop);
-
-	@net.sf.jni4net.attributes.ClrMethod("(LSystem/String;)V")
-	public native void setDevice(java.lang.String name);
-
-	@net.sf.jni4net.attributes.ClrMethod("()[LSystem/String;")
-	public native static java.lang.String[] getDevices();
-
-	@net.sf.jni4net.attributes.ClrMethod("()V")
-	public native void unload();
-
-	public static system.Type typeof() {
-		return nativeaudio.NativeAudio.staticType;
-	}
-
-	private static void InitJNI(net.sf.jni4net.inj.INJEnv env, system.Type staticType) {
-		nativeaudio.NativeAudio.staticType = staticType;
-	}
-	//</generated-proxy>
+    
+    //<generated-proxy>
+    private static system.Type staticType;
+    
+    protected NativeAudio(net.sf.jni4net.inj.INJEnv __env, long __handle) {
+            super(__env, __handle);
+    }
+    
+    @net.sf.jni4net.attributes.ClrConstructor("()V")
+    public NativeAudio() {
+            super(((net.sf.jni4net.inj.INJEnv)(null)), 0);
+        nativeaudio.NativeAudio.__ctorNativeAudio0(this);
+    }
+    
+    @net.sf.jni4net.attributes.ClrMethod("()V")
+    private native static void __ctorNativeAudio0(net.sf.jni4net.inj.IClrProxy thiz);
+    
+    @net.sf.jni4net.attributes.ClrMethod("(LSystem/String;)Z")
+    public native boolean load(java.lang.String path);
+    
+    @net.sf.jni4net.attributes.ClrMethod("()V")
+    public native void play();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()V")
+    public native void pause();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()V")
+    public native void stop();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()D")
+    public native double getDuration();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()D")
+    public native double getPosition();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()Z")
+    public native boolean isPlaying();
+    
+    @net.sf.jni4net.attributes.ClrMethod("(J)V")
+    public native void seek(long duration);
+    
+    @net.sf.jni4net.attributes.ClrMethod("(F)V")
+    public native void setVolume(float volume);
+    
+    @net.sf.jni4net.attributes.ClrMethod("(Z)V")
+    public native void setLoop(boolean loop);
+    
+    @net.sf.jni4net.attributes.ClrMethod("(LSystem/String;)V")
+    public native void setDevice(java.lang.String name);
+    
+    @net.sf.jni4net.attributes.ClrMethod("()[LSystem/String;")
+    public native static java.lang.String[] getDevices();
+    
+    @net.sf.jni4net.attributes.ClrMethod("()V")
+    public native void unload();
+    
+    public static system.Type typeof() {
+        return nativeaudio.NativeAudio.staticType;
+    }
+    
+    private static void InitJNI(net.sf.jni4net.inj.INJEnv env, system.Type staticType) {
+        nativeaudio.NativeAudio.staticType = staticType;
+    }
+    //</generated-proxy>
 }
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/pom.xml b/PlayWallPlugins/PlayWallPluginNativeAudio/pom.xml
index b2c4941c26d77e0b6aa485cf423b606017803b0e..8063a7c57ad420c319ea3ba7a142d783e6448e13 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/pom.xml
@@ -2,25 +2,24 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
 
 
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>PlayWallPluginNativeAudio</artifactId>
 
     <properties>
-        <jni4net.j.version>0.8.8.0</jni4net.j.version>
         <nativeAudio.version>1.0.0</nativeAudio.version>
 
         <project.outputDirectory>../../build/${project.version}</project.outputDirectory>
         <project.artifactName>${project.artifactId}-v${project.version}</project.artifactName>
     </properties>
 
-    <artifactId>PlayWallPluginNativeAudio</artifactId>
-
     <dependencies>
         <dependency>
             <groupId>de.tobias.playpad</groupId>
@@ -29,11 +28,6 @@
             <scope>provided</scope>
         </dependency>
 
-        <dependency>
-            <groupId>net.sf.jni4net</groupId>
-            <artifactId>jni4net.j</artifactId>
-            <version>${jni4net.j.version}</version>
-        </dependency>
         <dependency>
             <groupId>de.tobias.playpad</groupId>
             <artifactId>NativeAudioWin</artifactId>
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandler.java b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandler.java
index 0e94b7cbc3e8cea525918de0eed9cd6bf12a433f..82507228ff7cf523fb50fa2a799baf54251488ca 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandler.java
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandler.java
@@ -20,14 +20,14 @@ public class NativeAudioMacHandler extends AudioHandler implements Peakable, See
 
 	public static final String SOUND_CARD = "SoundCardMac";
 
-	private ObjectProperty<Duration> positionProperty;
-	private ObjectProperty<Duration> durationProperty;
+	private final ObjectProperty<Duration> positionProperty;
+	private final ObjectProperty<Duration> durationProperty;
 	private boolean isLoaded;
 
-	private DoubleProperty leftPeak;
-	private DoubleProperty rightPeak;
+	private final DoubleProperty leftPeak;
+	private final DoubleProperty rightPeak;
 
-	private AVAudioPlayerBridge bridge;
+	private final AVAudioPlayerBridge bridge;
 
 	NativeAudioMacHandler(PadContent content) {
 		super(content);
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandlerFactory.java b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandlerFactory.java
index 732b79ac4f6f405700410fc48ba279b620e235d0..5258e5534fb462d02daa3c728e6d98ee493b578a 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandlerFactory.java
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/NativeAudioMacHandlerFactory.java
@@ -15,8 +15,8 @@ import java.util.Optional;
 
 public class NativeAudioMacHandlerFactory extends AudioHandlerFactory {
 
-	private List<NativeAudioMacHandler> handlers = new ArrayList<>();
-	private AVAudioPlayerBridgeDelegate bridgeDelegate = new AVAudioPlayerBridgeDelegate(this);
+	private final List<NativeAudioMacHandler> handlers = new ArrayList<>();
+	private final AVAudioPlayerBridgeDelegate bridgeDelegate = new AVAudioPlayerBridgeDelegate(this);
 
 	public Optional<NativeAudioMacHandler> getHandlerByBridge(AVAudioPlayerBridge bridge) {
 		return handlers.stream().filter(handler -> handler.getBridge().equals(bridge)).findFirst();
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/delegate/AVAudioPlayerBridgeDelegate.java b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/delegate/AVAudioPlayerBridgeDelegate.java
index 4b6ec1af52a3b785e1428c8b24234bcadd981267..a0ad54a505015d0421ce863e5a09b98870bd48b9 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/delegate/AVAudioPlayerBridgeDelegate.java
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/java/de/tobias/playpad/audio/mac/delegate/AVAudioPlayerBridgeDelegate.java
@@ -12,7 +12,7 @@ import java.util.Optional;
 
 public class AVAudioPlayerBridgeDelegate implements AVAudioPlayerBridge.NativeAudioDelegate {
 
-	private NativeAudioMacHandlerFactory factory;
+	private final NativeAudioMacHandlerFactory factory;
 
 	public AVAudioPlayerBridgeDelegate(NativeAudioMacHandlerFactory factory) {
 		this.factory = factory;
@@ -24,6 +24,7 @@ public class AVAudioPlayerBridgeDelegate implements AVAudioPlayerBridge.NativeAu
 		nativeAudioMacHandler.ifPresent(handler -> {
 			PadContent content = handler.getContent();
 			if (content != null) {
+				content.getPad().setEof(true);
 				content.getPad().setStatus(PadStatus.STOP);
 			}
 		});
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/plugin.yml
index 234869dcc947e61bc19b7d861a80a4b3c8b02d42..943f266735fa147db3a63c3dfe64be356264a9ad 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "NativeAudio"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 2
+build: 3
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.dll b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.dll
index bac58da0e22e4913990f86819a4043a21ed58fbd..681575270f41d99b3d9e75e025344d9eed5b6fdc 100755
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.dll and b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.dll b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.dll
index 97fa3d253f0980c173c27bb0c1431bd0092a5bdd..c4193f420d4a1b554c9db5cef5bd85e78f2dc025 100755
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.dll and b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.dll differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.jar b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.jar
deleted file mode 100755
index 84f92d5c6577cfbd44199ab0f36d18b7afe6b5a1..0000000000000000000000000000000000000000
Binary files a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/resources/win/NativeAudio.j4n.jar and /dev/null differ
diff --git a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/scala/de/tobias/playpad/plugin/loader/WindowsAudioImplLoader.scala b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/scala/de/tobias/playpad/plugin/loader/WindowsAudioImplLoader.scala
index e808b2bacaafe1104b9e517739dae32b8b17816e..ab6a7d2a7b5e0ccd3db07e9f3187d1a94025839e 100644
--- a/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/scala/de/tobias/playpad/plugin/loader/WindowsAudioImplLoader.scala
+++ b/PlayWallPlugins/PlayWallPluginNativeAudio/src/main/scala/de/tobias/playpad/plugin/loader/WindowsAudioImplLoader.scala
@@ -1,46 +1,18 @@
 package de.tobias.playpad.plugin.loader
 
-import java.nio.file.Files
-
-import de.thecodelabs.utils.application.container.PathType
-import de.thecodelabs.utils.application.{App, ApplicationUtils}
 import de.tobias.playpad.PlayPadPlugin
 import de.tobias.playpad.audio.windows.NativeAudioWinHandlerFactory
-import de.tobias.playpad.plugin.Module
-import net.sf.jni4net.Bridge
+import de.tobias.playpad.plugin.{Jni4NetBridgeInitializer, Module}
 
 /**
   * Created by tobias on 16.04.17.
   */
 class WindowsAudioImplLoader extends AudioModuleLoader {
 
-	val resources = Array(
-		"jni4net.j-0.8.8.0.jar",
-		"jni4net.n-0.8.8.0.dll",
-		"jni4net.n.w32.v40-0.8.8.0.dll",
-		"jni4net.n.w64.v40-0.8.8.0.dll",
-		"NativeAudio.dll",
-		"NativeAudio.j4n.dll",
-		"NativeAudio.j4n.jar",
-		"NAudio.dll"
-	)
-
-	private val ASSETS = "win/"
-
 	override def preInit(): Unit = {
-		val app: App = ApplicationUtils.getApplication
-		val resourceFolder = app.getPath(PathType.LIBRARY, "NativeAudio")
-
-		if (!app.isDebug) {
-			if (Files.notExists(resourceFolder))
-				Files.createDirectories(resourceFolder)
-
-			resources.foreach(copyResource(resourceFolder, ASSETS, _))
-		}
-
-		Bridge.setVerbose(app.isDebug)
-		Bridge.init(resourceFolder.toFile)
-		Bridge.LoadAndRegisterAssemblyFrom(resourceFolder.resolve("NativeAudio.j4n.dll").toFile)
+		Jni4NetBridgeInitializer.initialize()
+		Jni4NetBridgeInitializer.loadDll(getClass.getClassLoader, "win/", "j4n", "NativeAudio.j4n.dll",
+			"NativeAudio.j4n.dll", "NativeAudio.dll", "NAudio.dll")
 	}
 
 	override def init(module: Module): Unit = {
diff --git a/PlayWallPlugins/PlayWallPluginPlayoutLog/pom.xml b/PlayWallPlugins/PlayWallPluginPlayoutLog/pom.xml
index 2239ae0009ea855a08acdffc80c1756c3c5bc091..d0f13b17074e82c3b1a47528b62bb90d364ef079 100644
--- a/PlayWallPlugins/PlayWallPluginPlayoutLog/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginPlayoutLog/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallPlugins</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java/de/tobias/playpad/plugin/playout/log/listener/PadPlayLogListener.java b/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java/de/tobias/playpad/plugin/playout/log/listener/PadPlayLogListener.java
index 05ce0b71bd3f45bcb790fdf0f8fae52ccd1b6354..b8c29217b59ced49d503313a016b91992f461e67 100644
--- a/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java/de/tobias/playpad/plugin/playout/log/listener/PadPlayLogListener.java
+++ b/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/java/de/tobias/playpad/plugin/playout/log/listener/PadPlayLogListener.java
@@ -14,7 +14,12 @@ import javafx.collections.ListChangeListener;
 public class PadPlayLogListener implements PadListener {
 
 	@Override
-	public void onStatusChange(Pad pad, PadStatus newValue) {
+	public void onNameChanged(Pad pad, String oldValue, String newValue) {
+		// Nothing to implement
+	}
+
+	@Override
+	public void onStatusChange(Pad pad, PadStatus oldValue, PadStatus newValue) {
 		if (newValue == PadStatus.PLAY) {
 			LogSeason instance = LogSeasons.getCurrentSession();
 			if (instance != null) {
diff --git a/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources/plugin.yml
index 1375cac9133e7a3877657879559bf9d63570b750..73a7743e3f90beea69e567240245236e10a403bd 100644
--- a/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginPlayoutLog/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "PlayoutLogPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 9
+build: 10
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/pom.xml b/PlayWallPlugins/PlayWallPluginWebAPI/pom.xml
index d0b4ab9b7c8d5cd258d926eec7f1858becfd855b..b905d9187c28c41e8cd3ff3433f960cd412bda46 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/pom.xml
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/pom.xml
@@ -3,9 +3,9 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>PlayWallPlugins</artifactId>
         <groupId>de.tobias.playpad</groupId>
-        <version>7.1.0</version>
+        <artifactId>PlayWallPlugins</artifactId>
+        <version>7.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,6 +29,11 @@
             <artifactId>spark-core</artifactId>
             <version>${spark-core.version}</version>
         </dependency>
+        <dependency>
+            <groupId>de.tobias.playpad</groupId>
+            <artifactId>PlayWallWebApiClient</artifactId>
+            <version>${PlayWallWebApiClient.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/cell/WebApiRemoteCell.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/cell/WebApiRemoteCell.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8b421e5b45bb2a36e9b6a8101fef0b624b18b5c
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/cell/WebApiRemoteCell.java
@@ -0,0 +1,17 @@
+package de.tobias.playpad.plugin.api.cell;
+
+import de.tobias.playpad.plugin.api.settings.WebApiRemoteSettings;
+import javafx.scene.control.ListCell;
+
+public class WebApiRemoteCell extends ListCell<WebApiRemoteSettings> {
+
+	@Override
+	protected void updateItem(WebApiRemoteSettings item, boolean empty) {
+		super.updateItem(item, empty);
+		if (!empty) {
+			setText(item.getName());
+		} else {
+			setText("");
+		}
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b1d2b8c5ee2bdf2b546517ce94ad5ab47d2e6ad
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/client/WebApiRemoteConnectionStateListener.java
@@ -0,0 +1,98 @@
+package de.tobias.playpad.plugin.api.client;
+
+import com.neovisionaries.ws.client.WebSocketException;
+import de.thecodelabs.logger.Logger;
+import de.thecodelabs.utils.threading.Worker;
+import de.thecodelabs.utils.ui.icon.FontAwesomeType;
+import de.thecodelabs.utils.ui.icon.FontIcon;
+import de.thecodelabs.utils.util.Localization;
+import de.tobias.playpad.api.PlayPadClient;
+import de.tobias.playpad.api.PlayPadConnectionState;
+import de.tobias.playpad.plugin.MainWindowListener;
+import de.tobias.playpad.plugin.api.WebApiPlugin$;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+import javafx.application.Platform;
+import javafx.beans.InvalidationListener;
+import javafx.beans.binding.Bindings;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class WebApiRemoteConnectionStateListener implements MainWindowListener {
+
+	private final FontIcon connectionStateIcon;
+	private final IntegerProperty connectedProperty;
+
+	public WebApiRemoteConnectionStateListener() {
+		connectionStateIcon = new FontIcon(FontAwesomeType.CLOUD);
+		connectionStateIcon.setSize(20);
+
+		connectionStateIcon.setOnMouseClicked(this::onIconClicked);
+
+		connectedProperty = new SimpleIntegerProperty(0);
+		connectedProperty.addListener((observable, oldValue, newValue) -> {
+			Platform.runLater(() -> {
+				boolean allConnected = newValue.intValue() == 0;
+				connectionStateIcon.setIcons(allConnected ? FontAwesomeType.CLOUD : FontAwesomeType.EXCLAMATION_CIRCLE);
+				if (!allConnected) {
+					final String disconnectedServers = WebApiPlugin$.MODULE$.connections().entrySet().stream()
+							.filter(entry -> entry.getValue().getPlayPadConnectionState() != PlayPadConnectionState.CONNECTED)
+							.map(entry -> entry.getKey().getName()).collect(Collectors.joining(", "));
+					connectionStateIcon.setTooltip(new Tooltip(Localization.getString("webapi-settings.remote.state.tooltip", disconnectedServers)));
+					connectionStateIcon.setStyle("-fx-text-fill: red;");
+				} else {
+					connectionStateIcon.setTooltip(null);
+					connectionStateIcon.setStyle("");
+				}
+			});
+		});
+
+		WebApiPlugin$.MODULE$.connections().addListener((InvalidationListener) observable -> {
+			createConnectionStateBinding();
+			connectionStateIcon.setVisible(!WebApiPlugin$.MODULE$.connections().isEmpty());
+		});
+		connectionStateIcon.setVisible(!WebApiPlugin$.MODULE$.connections().isEmpty());
+		createConnectionStateBinding();
+	}
+
+	private void createConnectionStateBinding() {
+		final List<PlayPadClient> clients = new ArrayList<>(WebApiPlugin$.MODULE$.connections().values());
+		connectedProperty.bind(Bindings.createIntegerBinding(() -> (int) WebApiPlugin$.MODULE$.connections().values().stream()
+						.filter(client -> client.getPlayPadConnectionState() != PlayPadConnectionState.CONNECTED)
+						.count(),
+				clients.stream()
+						.map(PlayPadClient::playPadConnectionState)
+						.toArray(ObjectProperty[]::new))
+		);
+	}
+
+	private void onIconClicked(MouseEvent event) {
+		WebApiPlugin$.MODULE$.connections().values().stream()
+				.filter(client -> client.getPlayPadConnectionState() != PlayPadConnectionState.CONNECTED)
+				.forEach(client -> Worker.runLater(() -> {
+					try {
+						client.disconnect();
+						client.connect(1);
+					} catch (IOException | WebSocketException e) {
+						Logger.error(e);
+					}
+				}));
+	}
+
+	@Override
+	public void onInit(IMainViewController mainViewController) {
+		mainViewController.performLayoutDependedAction((oldToolbar, newToolbar) -> {
+			if (oldToolbar != null) {
+				oldToolbar.removeToolbarItem(connectionStateIcon);
+			}
+			newToolbar.addToolbarItem(connectionStateIcon);
+		});
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiRemoteSettings.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiRemoteSettings.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3836667a59b5d1899fb47e06153267a38e6913f
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiRemoteSettings.java
@@ -0,0 +1,75 @@
+package de.tobias.playpad.plugin.api.settings;
+
+import de.thecodelabs.storage.settings.annotation.Key;
+import de.tobias.playpad.Displayable;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import java.util.Objects;
+import java.util.UUID;
+
+public class WebApiRemoteSettings implements Displayable {
+	@Key
+	private UUID id = UUID.randomUUID();
+	@Key
+	private String name;
+	@Key
+	private String serverAddress;
+	@Key
+	private int port;
+
+	public UUID getId() {
+		return id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+		displayProperty.set(name);
+	}
+
+	public String getServerAddress() {
+		return serverAddress;
+	}
+
+	public void setServerAddress(String serverAddress) {
+		this.serverAddress = serverAddress;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	private final StringProperty displayProperty = new SimpleStringProperty();
+
+	@Override
+	public StringProperty displayProperty() {
+		displayProperty.set(name);
+		return displayProperty;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) {
+			return true;
+		}
+		if (!(o instanceof WebApiRemoteSettings)) {
+			return false;
+		}
+		WebApiRemoteSettings that = (WebApiRemoteSettings) o;
+		return port == that.port && Objects.equals(id, that.id) && Objects.equals(name, that.name) &&
+				Objects.equals(serverAddress, that.serverAddress);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(id, name, serverAddress, port);
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettings.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiSettings.java
similarity index 55%
rename from PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettings.java
rename to PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiSettings.java
index c5b35f5f180fa76bf0c1f9681e96c9b03f30f4c5..0d34d9646560107795bef9092169af02fd25b311 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettings.java
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/settings/WebApiSettings.java
@@ -1,14 +1,19 @@
-package de.tobias.playpad.plugin.api.websocket.settings;
+package de.tobias.playpad.plugin.api.settings;
 
 import de.thecodelabs.storage.settings.annotation.FilePath;
 import de.thecodelabs.storage.settings.annotation.Key;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @FilePath("webapi.json")
 public class WebApiSettings {
 	@Key
 	private boolean enabled = false;
 	@Key
 	private int port = 9876;
+	@Key
+	private List<WebApiRemoteSettings> remoteSettings = new ArrayList<>();
 
 	public boolean isEnabled() {
 		return enabled;
@@ -25,4 +30,12 @@ public class WebApiSettings {
 	public void setPort(int port) {
 		this.port = port;
 	}
+
+	public List<WebApiRemoteSettings> getRemoteSettings() {
+		return remoteSettings;
+	}
+
+	public void setRemoteSettings(List<WebApiRemoteSettings> remoteSettings) {
+		this.remoteSettings = remoteSettings;
+	}
 }
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItem.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..64efe5093957340f795206bd81c35cbe43e81e2a
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItem.java
@@ -0,0 +1,108 @@
+package de.tobias.playpad.plugin.api.trigger;
+
+import de.thecodelabs.utils.list.UniqList;
+import de.tobias.playpad.pad.Pad;
+import de.tobias.playpad.pad.PadStatus;
+import de.tobias.playpad.plugin.api.WebApiPlugin$;
+import de.tobias.playpad.profile.Profile;
+import de.tobias.playpad.project.Project;
+import de.tobias.playpad.tigger.TriggerItem;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+import org.dom4j.Element;
+
+import java.util.List;
+import java.util.UUID;
+
+public class RemoteTriggerItem extends TriggerItem {
+
+	private final String type;
+
+	private UUID serverId;
+	private List<UUID> uuids;
+	private PadStatus newStatus;
+
+	public RemoteTriggerItem(String type) {
+		this.type = type;
+
+		this.newStatus = PadStatus.PLAY;
+		this.uuids = new UniqList<>();
+	}
+
+	public List<UUID> getCarts() {
+		return uuids;
+	}
+
+	public PadStatus getNewStatus() {
+		return newStatus;
+	}
+
+	public void setNewStatus(PadStatus newStatus) {
+		if (newStatus == PadStatus.PLAY || newStatus == PadStatus.PAUSE || newStatus == PadStatus.STOP)
+			this.newStatus = newStatus;
+	}
+
+	@Override
+	public String getType() {
+		return type;
+	}
+
+	@Override
+	public void performAction(Pad pad, Project project, IMainViewController controller, Profile profile) {
+		WebApiPlugin$.MODULE$.getConnection(serverId).ifPresent(client -> {
+			for (UUID uuid : uuids) {
+				client.setPadStatus(uuid, newStatus);
+			}
+		});
+	}
+
+	@Override
+	public TriggerItem copy() {
+		RemoteTriggerItem clone = new RemoteTriggerItem(getType());
+
+		clone.uuids = new UniqList<>();
+		clone.uuids.addAll(uuids);
+		clone.newStatus = newStatus;
+
+		return clone;
+	}
+
+	private static final String SERVER_ELEMENT = "Server";
+	private static final String CART_ELEMENT = "Cart";
+	private static final String STATUS_ATTR = "Status";
+
+	@Override
+	public void load(Element element) {
+		super.load(element);
+
+		if (element.attributeValue(SERVER_ELEMENT) != null)
+			setServerId(UUID.fromString(element.attributeValue(SERVER_ELEMENT)));
+
+		if (element.attributeValue(STATUS_ATTR) != null)
+			setNewStatus(PadStatus.valueOf(element.attributeValue(STATUS_ATTR)));
+
+		for (Element cartElement : element.elements(CART_ELEMENT)) {
+			uuids.add(UUID.fromString(cartElement.getStringValue()));
+		}
+	}
+
+	@Override
+	public void save(Element element) {
+		super.save(element);
+
+		element.addAttribute(SERVER_ELEMENT, serverId.toString());
+		element.addAttribute(STATUS_ATTR, newStatus.name());
+
+		for (UUID cart : uuids) {
+			Element cartElement = element.addElement(CART_ELEMENT);
+			cartElement.addText(String.valueOf(cart));
+		}
+	}
+
+	public UUID getServerId() {
+		return serverId;
+	}
+
+	public void setServerId(UUID serverId) {
+		this.serverId = serverId;
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemFactory.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..18d2033f1b812edb83ee29dccd9e6332b02ece51
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemFactory.java
@@ -0,0 +1,24 @@
+package de.tobias.playpad.plugin.api.trigger;
+
+import de.thecodelabs.utils.ui.NVC;
+import de.tobias.playpad.tigger.Trigger;
+import de.tobias.playpad.tigger.TriggerItem;
+import de.tobias.playpad.tigger.TriggerItemFactory;
+import de.tobias.playpad.viewcontroller.main.IMainViewController;
+
+public class RemoteTriggerItemFactory extends TriggerItemFactory {
+
+	public RemoteTriggerItemFactory(String type) {
+		super(type);
+	}
+
+	@Override
+	public TriggerItem newInstance(Trigger trigger) {
+		return new RemoteTriggerItem(getType());
+	}
+
+	@Override
+	public NVC getSettingsController(TriggerItem item, IMainViewController mainViewController) {
+		return new RemoteTriggerItemSettingsController((RemoteTriggerItem) item);
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemSettingsController.java b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemSettingsController.java
new file mode 100644
index 0000000000000000000000000000000000000000..36c14e8bcc007f4fffdd51cb51c0f28e6683b989
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/java/de/tobias/playpad/plugin/api/trigger/RemoteTriggerItemSettingsController.java
@@ -0,0 +1,89 @@
+package de.tobias.playpad.plugin.api.trigger;
+
+import de.thecodelabs.utils.ui.NVC;
+import de.thecodelabs.utils.util.Localization;
+import de.tobias.playpad.pad.PadStatus;
+import de.tobias.playpad.plugin.api.WebApiPlugin$;
+import de.tobias.playpad.plugin.api.cell.WebApiRemoteCell;
+import de.tobias.playpad.plugin.api.settings.WebApiRemoteSettings;
+import de.tobias.playpad.project.api.IPad;
+import de.tobias.playpad.view.main.ProjectPreviewView;
+import javafx.application.Platform;
+import javafx.beans.InvalidationListener;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.scene.control.ComboBox;
+import javafx.scene.layout.VBox;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class RemoteTriggerItemSettingsController extends NVC {
+	@FXML
+	private ComboBox<PadStatus> statusComboBox;
+	@FXML
+	private ComboBox<WebApiRemoteSettings> remoteComboBox;
+
+	private ProjectPreviewView projectPreviewView;
+
+	private final RemoteTriggerItem item;
+
+	public RemoteTriggerItemSettingsController(RemoteTriggerItem item) {
+		load("plugin/webapi/view", "RemoteTrigger", Localization.getBundle());
+		this.item = item;
+
+		showProjectPreview();
+
+		statusComboBox.setValue(item.getNewStatus());
+		remoteComboBox.setValue(WebApiPlugin$.MODULE$.connections().keySet().stream()
+				.filter(server -> server.getId().equals(item.getServerId()))
+				.findFirst()
+				.orElse(null)
+		);
+	}
+
+	private void showProjectPreview() {
+		WebApiPlugin$.MODULE$.getConnection(item.getServerId()).ifPresent(client -> {
+			client.getCurrentProject(project -> {
+				Platform.runLater(() -> {
+					// Remove old node from tree
+					if (projectPreviewView != null) {
+						((VBox) getParent()).getChildren().remove(projectPreviewView);
+						projectPreviewView = null;
+					}
+
+					final List<? extends IPad> preSelect = item.getCarts().stream()
+							.map(project::getPad)
+							.filter(Objects::nonNull)
+							.collect(Collectors.toList());
+
+					projectPreviewView = new ProjectPreviewView(project, preSelect, 0);
+					projectPreviewView.setPadding(new Insets(0, 0, 0, 164));
+					projectPreviewView.selectedProperty().addListener((InvalidationListener) observable -> {
+						item.getCarts().clear();
+						for (IPad pad : projectPreviewView.getSelected()) {
+							item.getCarts().add(pad.getUuid());
+						}
+					});
+					VBox vBox = (VBox) getParent();
+					vBox.getChildren().add(projectPreviewView);
+				});
+			});
+		});
+	}
+
+	@Override
+	public void init() {
+		statusComboBox.getItems().setAll(PadStatus.PLAY, PadStatus.PAUSE, PadStatus.STOP);
+		statusComboBox.valueProperty().addListener((a, b, c) -> item.setNewStatus(c));
+
+		remoteComboBox.getItems().setAll(WebApiPlugin$.MODULE$.connections().keySet());
+		remoteComboBox.setCellFactory(list -> new WebApiRemoteCell());
+		remoteComboBox.setButtonCell(new WebApiRemoteCell());
+		remoteComboBox.valueProperty().addListener((observable, oldValue, newValue) -> {
+			item.setServerId(newValue.getId());
+			showProjectPreview();
+		});
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin.yml b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin.yml
index 2f569fa8af980990863c4413821ee980ece9c130..98733bb6fe0d64b0837bcfbf0de96454b81a45e9 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin.yml
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin.yml
@@ -3,4 +3,4 @@ name: "WebApiPlugin"
 artifactId: "${pom.artifactId}"
 groupId: "${pom.groupId}"
 version: "${pom.version}"
-build: 2
+build: 3
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/Trigger.xml b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/Trigger.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5fb97b9bbdeeb4d568f8517e44e0c8c6dccdf801
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/Trigger.xml
@@ -0,0 +1,3 @@
+<Actions>
+    <Component id="RemoteCart" name="Trigger.RemoteCart.Name">de.tobias.playpad.plugin.api.trigger.RemoteTriggerItemFactory</Component>
+</Actions>
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
index 0b7b9cf8475b89313f3289e30aef44a2c86136a8..d9d9cbe45efb58aa31bffbccea3a91f8c4750069 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/lang/base_de.properties
@@ -1,5 +1,22 @@
 webapi.settings=WebAPI
-webapi.settings.enable=WebAPI
-webapi.settings.enabled=Aktiv
-webapi.settings.port=Port
-webapi.settings.restart=\u00C4nderungen an den Einstellungen werden erst mit einem Neustart von PlayWall wirksam.
\ No newline at end of file
+webapi-settings.server=Fernsteuerung
+webapi.settings.server.enable=WebAPI
+webapi.settings.server.enabled=Aktiv
+webapi.settings.server.port=Port
+webapi.settings.server.restart=\u00C4nderungen an den Einstellungen werden erst mit einem Neustart von PlayWall wirksam.
+
+webapi-settings.remote=PlayWall Instanzen fernsteuern
+webapi-settings.remote.state.tooltip=Keine Verbindung zu: {}
+webapi-settings.remote.name=Name
+webapi-settings.remote.address=IP-Address/Host
+webapi-settings.remote.port=Port
+webapi-settings.remote.add=Hinzuf\u00FCgen
+webapi-settings.remote.remove=L\u00F6schen
+
+webapi-settings.remote.error.connection=Fehler beim Verbinden zu PlayWall {} ({})
+
+Trigger.RemoteCart.Name=Remote Kachel
+
+remotetrigger.label.action=Aktion f\u00FCr Kacheln:
+remotetrigger.label.server=PlayWall Instanz
+remotetrigger.label.carts=Kacheln:
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/RemoteTrigger.fxml b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/RemoteTrigger.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..967b16dce38d431d497ed3115db7e6f12b4e2d53
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/RemoteTrigger.fxml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.ComboBox?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.VBox?>
+<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0"
+      xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
+    <children>
+        <HBox spacing="14.0">
+            <children>
+                <Label alignment="BASELINE_RIGHT" maxHeight="1.7976931348623157E308" prefWidth="150.0"
+                       text="%remotetrigger.label.action"/>
+                <ComboBox fx:id="statusComboBox" prefWidth="150.0"/>
+            </children>
+        </HBox>
+        <HBox spacing="14.0">
+            <children>
+                <Label alignment="BASELINE_RIGHT" maxHeight="1.7976931348623157E308" prefWidth="150.0"
+                       text="%remotetrigger.label.server"/>
+                <ComboBox fx:id="remoteComboBox" prefWidth="150.0"/>
+            </children>
+        </HBox>
+    </children>
+</VBox>
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/WebApiSettings.fxml b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/WebApiSettings.fxml
index 473dba8d1a6441e62fed11fa04b87a6be18397b3..81b0eab7541cd2f0455959b117ffe28399ac9b9e 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/WebApiSettings.fxml
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/resources/plugin/webapi/view/WebApiSettings.fxml
@@ -2,32 +2,69 @@
 
 <?import de.thecodelabs.utils.ui.scene.input.NumberTextField?>
 <?import javafx.geometry.Insets?>
-<?import javafx.scene.control.CheckBox?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.layout.HBox?>
-<?import javafx.scene.layout.VBox?>
-<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0"
-      xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="14.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
     <children>
+        <Label text="%webapi-settings.server" underline="true" />
         <HBox alignment="CENTER_LEFT" spacing="14.0">
             <children>
-                <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%webapi.settings.enable"/>
-                <CheckBox fx:id="activeCheckbox" mnemonicParsing="false" text="%webapi.settings.enabled"/>
+                <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%webapi.settings.server.enable" />
+                <CheckBox fx:id="activeCheckbox" mnemonicParsing="false" text="%webapi.settings.server.enabled" />
             </children>
         </HBox>
         <HBox alignment="CENTER_LEFT" spacing="14.0">
             <children>
-                <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%webapi.settings.port"/>
-                <NumberTextField fx:id="portTextField"/>
+                <Label alignment="CENTER_RIGHT" prefWidth="150.0" text="%webapi.settings.server.port" />
+                <NumberTextField fx:id="portTextField" />
             </children>
         </HBox>
-        <Label text="%webapi.settings.restart">
+        <Label text="%webapi.settings.server.restart">
             <padding>
-                <Insets left="164.0"/>
+                <Insets left="164.0" />
             </padding>
         </Label>
+      <Separator prefWidth="200.0" />
+      <Label text="%webapi-settings.remote" underline="true" />
+      <HBox spacing="14.0" VBox.vgrow="ALWAYS">
+         <children>
+            <VBox spacing="14.0">
+               <children>
+                  <ListView fx:id="remoteListView" VBox.vgrow="ALWAYS" />
+                  <HBox spacing="14.0">
+                     <children>
+                        <Button fx:id="remoteAddButton" onAction="#onRemoteAddButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="%webapi-settings.remote.add" HBox.hgrow="ALWAYS" />
+                        <Button fx:id="remoteDeleteButton" onAction="#onRemoteDeleteButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="%webapi-settings.remote.remove" HBox.hgrow="ALWAYS" />
+                     </children>
+                  </HBox>
+               </children>
+            </VBox>
+            <VBox spacing="14.0">
+               <children>
+                  <HBox alignment="CENTER_LEFT" spacing="14.0">
+                     <children>
+                        <Label alignment="CENTER_RIGHT" prefWidth="100.0" text="%webapi-settings.remote.name" />
+                        <TextField fx:id="remoteNameTextField" />
+                     </children>
+                  </HBox>
+                  <HBox alignment="CENTER_LEFT" spacing="14.0">
+                     <children>
+                        <Label alignment="CENTER_RIGHT" prefWidth="100.0" text="%webapi-settings.remote.address" />
+                        <TextField fx:id="remoteAddressTextField" />
+                     </children>
+                  </HBox>
+                  <HBox alignment="CENTER_LEFT" spacing="14.0">
+                     <children>
+                        <Label alignment="CENTER_RIGHT" prefWidth="100.0" text="%webapi-settings.remote.port" />
+                        <NumberTextField fx:id="remotePortTextField" />
+                     </children>
+                  </HBox>
+               </children>
+            </VBox>
+         </children>
+      </HBox>
     </children>
     <padding>
-        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0"/>
+        <Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
     </padding>
 </VBox>
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
index 0d4d7321ee3a880471919572daaea62d1da8517d..900bedfc13678eadaa0b0b07e3dfa16a79724eb0 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/WebApiPlugin.scala
@@ -1,21 +1,31 @@
 package de.tobias.playpad.plugin.api
 
-import java.nio.file.{Files, Path}
-
 import de.thecodelabs.logger.Logger
 import de.thecodelabs.plugins.PluginDescriptor
 import de.thecodelabs.plugins.versionizer.PluginArtifact
 import de.thecodelabs.storage.settings.{Storage, StorageTypes}
 import de.thecodelabs.utils.application.ApplicationUtils
 import de.thecodelabs.utils.application.container.PathType
+import de.thecodelabs.utils.threading.Worker
+import de.thecodelabs.utils.ui.Alerts
 import de.thecodelabs.utils.util.Localization
 import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.api.{PlayPadClient, PlayPadClientImpl}
+import de.tobias.playpad.plugin.api.WebApiPlugin.connectToRemoteInstances
+import de.tobias.playpad.plugin.api.client.WebApiRemoteConnectionStateListener
+import de.tobias.playpad.plugin.api.settings.{WebApiRemoteSettings, WebApiSettings, WebApiSettingsViewController}
 import de.tobias.playpad.plugin.api.websocket.WebSocketHandler
-import de.tobias.playpad.plugin.api.websocket.listener.{PadStatusListener, ProjectListener}
-import de.tobias.playpad.plugin.api.websocket.settings.{WebApiSettings, WebApiSettingsViewController}
+import de.tobias.playpad.plugin.api.websocket.listener.{MainWindowPageListener, PadApiListener, ProjectListener}
 import de.tobias.playpad.plugin.{Module, PlayPadPluginStub}
+import javafx.application.Platform
+import javafx.collections.{FXCollections, ObservableMap}
+import javafx.scene.control.Alert.AlertType
 import spark.{Request, Response, Spark}
 
+import java.nio.file.{Files, Path}
+import java.util
+import java.util.{Optional, UUID}
+
 class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 
 	private var module: Module = _
@@ -24,12 +34,16 @@ class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 
 	override def startup(descriptor: PluginDescriptor): Unit = {
 		module = new Module(descriptor.getName, descriptor.getArtifactId)
+		Localization.addResourceBundle("plugin/webapi/lang/base", getClass.getClassLoader)
 
-		PlayPadPlugin.getInstance().addPadListener(new PadStatusListener)
+		PlayPadPlugin.getInstance().addPadListener(new PadApiListener)
 		PlayPadPlugin.getInstance().addGlobalListener(new ProjectListener)
+		PlayPadPlugin.getInstance().addMainViewListener(new MainWindowPageListener)
 
 		Logger.debug("Enable Web API Plugin")
 
+		PlayPadPlugin.getRegistries.getTriggerItems.loadComponentsFromFile("plugin/webapi/Trigger.xml", getClass.getClassLoader, module, Localization.getBundle)
+
 		val settingsPath = WebApiPlugin.getWebApiSettingsPath
 		if (Files.exists(settingsPath)) {
 			webApiSettings = Storage.load(StorageTypes.JSON, classOf[WebApiSettings])
@@ -45,11 +59,14 @@ class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 			Logger.info(f"Start WebAPI on port ${webApiSettings.getPort}")
 		}
 
+		connectToRemoteInstances(webApiSettings)
+
 		PlayPadPlugin.getInstance().addGlobalSettingsTab(() => new WebApiSettingsViewController(webApiSettings))
-		Localization.addResourceBundle("plugin/webapi/lang/base", getClass.getClassLoader)
+		PlayPadPlugin.getInstance().addMainViewListener(new WebApiRemoteConnectionStateListener)
 	}
 
 	override def shutdown(): Unit = {
+		WebApiPlugin.disconnectAllInstances()
 		Spark.stop()
 		Logger.debug("Disable Web API Plugin")
 	}
@@ -60,4 +77,38 @@ class WebApiPlugin extends PlayPadPluginStub with PluginArtifact {
 
 object WebApiPlugin {
 	def getWebApiSettingsPath: Path = ApplicationUtils.getApplication.getPath(PathType.CONFIGURATION, "webapi.json")
+
+	var connections: ObservableMap[WebApiRemoteSettings, PlayPadClient] = FXCollections.observableMap(
+		new util.HashMap[WebApiRemoteSettings, PlayPadClient]())
+
+	def getConnection(id: UUID): Optional[PlayPadClient] = {
+		connections.entrySet().stream().filter(entry => entry.getKey.getId == id).findFirst().map(_.getValue)
+	}
+
+	def disconnectAllInstances(): Unit = {
+		connections.values().forEach(_.disconnect())
+		connections.clear()
+	}
+
+	def connectToRemoteInstances(webApiSettings: WebApiSettings): Unit = {
+		webApiSettings.getRemoteSettings.forEach(remote => {
+			Worker.runLater(() => {
+				try {
+					val client = new PlayPadClientImpl(f"ws://${remote.getServerAddress}:${remote.getPort}/api")
+					WebApiPlugin.connections.put(remote, client)
+					client.playPadConnectionState.addListener((_, _, newValue) => println(f"State: ${remote.getName} \t $newValue"))
+					client.connect(5)
+					Logger.info(s"Connected to remote PlayWall: ${remote.getName}")
+				} catch {
+					case e: Exception =>
+						Logger.error(e)
+						Platform.runLater(() => {
+							Alerts.getInstance().createAlert(AlertType.ERROR, null,
+								Localization.getString("webapi-settings.remote.error.connection", remote.getName, e.toString))
+								.show()
+						})
+				}
+			})
+		})
+	}
 }
\ No newline at end of file
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala
new file mode 100644
index 0000000000000000000000000000000000000000..02b9a4df588710c3a63673cdd3111931895efd44
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/settings/WebApiSettingsViewController.scala
@@ -0,0 +1,142 @@
+package de.tobias.playpad.plugin.api.settings
+
+import de.thecodelabs.logger.Logger
+import de.thecodelabs.storage.settings.{Storage, StorageTypes}
+import de.thecodelabs.utils.ui.scene.input.NumberTextField
+import de.thecodelabs.utils.util.Localization
+import de.tobias.playpad.plugin.api.WebApiPlugin
+import de.tobias.playpad.settings.GlobalSettings
+import de.tobias.playpad.viewcontroller.main.IMainViewController
+import de.tobias.playpad.viewcontroller.option.{GlobalSettingsTabViewController, IGlobalReloadTask}
+import javafx.event.ActionEvent
+import javafx.fxml.FXML
+import javafx.scene.control._
+
+import java.nio.file.Files
+
+class WebApiSettingsViewController(val webApiSettings: WebApiSettings) extends GlobalSettingsTabViewController with IGlobalReloadTask {
+
+	load("plugin/webapi/view", "WebApiSettings", Localization.getBundle)
+
+	private var startHash: Int = _
+
+	@FXML var activeCheckbox: CheckBox = _
+	@FXML var portTextField: NumberTextField = _
+
+	@FXML var remoteListView: ListView[WebApiRemoteSettings] = _
+	@FXML var remoteAddButton: Button = _
+	@FXML var remoteDeleteButton: Button = _
+	@FXML var remoteNameTextField: TextField = _
+	@FXML var remoteAddressTextField: TextField = _
+	@FXML var remotePortTextField: NumberTextField = _
+
+	override def init(): Unit = {
+		remoteListView.getItems.setAll(webApiSettings.getRemoteSettings)
+		remoteDeleteButton.disableProperty().bind(remoteListView.getSelectionModel.selectedItemProperty().isNull)
+
+		remoteListView.setCellFactory((_: ListView[WebApiRemoteSettings]) => new ListCell[WebApiRemoteSettings] {
+			override def updateItem(item: WebApiRemoteSettings, empty: Boolean): Unit = {
+				super.updateItem(item, empty)
+				if (!empty) {
+					textProperty().bind(item.displayProperty())
+				} else {
+					textProperty().unbind()
+					setText("")
+				}
+			}
+		})
+		remoteListView.getSelectionModel.selectedItemProperty().addListener((_, oldValue, newValue) => {
+			if (oldValue != null) {
+				saveSettingsToRemoteList(oldValue)
+			}
+			if (newValue != null) {
+				showRemoteConfiguration(newValue)
+			} else {
+				clearTextFields()
+			}
+		})
+
+		remoteNameTextField.disableProperty().bind(remoteListView.getSelectionModel.selectedItemProperty().isNull)
+		remoteAddressTextField.disableProperty().bind(remoteListView.getSelectionModel.selectedItemProperty().isNull)
+		remotePortTextField.disableProperty().bind(remoteListView.getSelectionModel.selectedItemProperty().isNull)
+	}
+
+	private def saveSettingsToRemoteList(remote: WebApiRemoteSettings): Unit = {
+		remote.setName(remoteNameTextField.getText)
+		remote.setServerAddress(remoteAddressTextField.getText)
+		remote.setPort(remotePortTextField.getText.toInt)
+	}
+
+
+	private def showRemoteConfiguration(remote: WebApiRemoteSettings): Unit = {
+		remoteNameTextField.setText(remote.getName)
+		remoteAddressTextField.setText(remote.getServerAddress)
+		remotePortTextField.setText(remote.getPort.toString)
+	}
+
+	private def clearTextFields(): Unit = {
+		remoteNameTextField.setText("")
+		remoteAddressTextField.setText("")
+		remotePortTextField.setText("")
+	}
+
+	@FXML def onRemoteAddButton(event: ActionEvent): Unit = {
+		val remoteSettings = new WebApiRemoteSettings()
+		remoteSettings.setName("Default")
+		webApiSettings.getRemoteSettings.add(remoteSettings)
+
+		remoteListView.getItems.add(remoteSettings)
+	}
+
+	@FXML def onRemoteDeleteButton(event: ActionEvent): Unit = {
+		val selectedItem = remoteListView.getSelectionModel.getSelectedItem
+		if (selectedItem != null) {
+			remoteListView.getItems.remove(selectedItem)
+			webApiSettings.getRemoteSettings.remove(selectedItem)
+		}
+	}
+
+	override def loadSettings(settings: GlobalSettings): Unit = {
+		startHash = webApiSettings.getRemoteSettings.hashCode()
+
+		activeCheckbox.setSelected(webApiSettings.isEnabled)
+		portTextField.setText(webApiSettings.getPort.toString)
+	}
+
+	override def saveSettings(settings: GlobalSettings): Unit = {
+		webApiSettings.setEnabled(activeCheckbox.isSelected)
+		webApiSettings.setPort(portTextField.getText.toInt)
+
+		val selectedItem = remoteListView.getSelectionModel.getSelectedItem
+		if (selectedItem != null) {
+			saveSettingsToRemoteList(selectedItem)
+		}
+
+		saveToFile()
+	}
+
+	override def needReload(): Boolean = startHash != webApiSettings.getRemoteSettings.hashCode()
+
+	override def getTask(settings: GlobalSettings, controller: IMainViewController): Runnable = () => {
+		Logger.info("Disconnect all remote instances")
+		WebApiPlugin.disconnectAllInstances()
+
+		Logger.info("Connect to new remote instances")
+		WebApiPlugin.connectToRemoteInstances(webApiSettings)
+	}
+
+	override def validSettings(): Boolean = {
+		true
+	}
+
+	override def name(): String = Localization.getString("webapi.settings")
+
+	private def saveToFile(): Unit = {
+		val settingsPath = WebApiPlugin.getWebApiSettingsPath
+		if (Files.notExists(settingsPath)) {
+			Files.createDirectories(settingsPath.getParent)
+		}
+		Storage.save(StorageTypes.JSON, webApiSettings)
+		Logger.info(f"Save WebAPI settings to $settingsPath")
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/WebSocketHandler.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/WebSocketHandler.scala
index 69b48550f1f043cf02bfea30e3116b3d79c11727..577d93a7fe3809a9cd29e7f342aa9bf4d7756377 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/WebSocketHandler.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/WebSocketHandler.scala
@@ -1,13 +1,13 @@
 package de.tobias.playpad.plugin.api.websocket
 
-import java.util.concurrent.ConcurrentLinkedQueue
-
 import com.google.gson.{Gson, JsonObject}
 import de.thecodelabs.logger.Logger
 import de.tobias.playpad.plugin.api.websocket.message.Message
-import de.tobias.playpad.plugin.api.websocket.methods.{PadStatusChangeMethod, ProjectCurrentMethod, ProjectListMethod, ProjectOpenMethod}
-import org.eclipse.jetty.websocket.api.Session
+import de.tobias.playpad.plugin.api.websocket.methods._
 import org.eclipse.jetty.websocket.api.annotations._
+import org.eclipse.jetty.websocket.api.{CloseException, Session}
+
+import java.util.concurrent.ConcurrentLinkedQueue
 
 @WebSocket
 class WebSocketHandler {
@@ -15,10 +15,16 @@ class WebSocketHandler {
 	private val sessions = new ConcurrentLinkedQueue[Session]
 
 	private val methods: Map[String, MethodExecutable] = Map(
+		"ping" -> new PingMethod,
 		"project-list" -> new ProjectListMethod,
 		"project-current" -> new ProjectCurrentMethod,
 		"project-open" -> new ProjectOpenMethod,
-		"pad-status-change" -> new PadStatusChangeMethod
+		"pad-status-change" -> new PadStatusChangeMethod,
+		"cart-action" -> new CartActionMethod,
+		"page-action" -> new PageActionMethod,
+		"navigate-action" -> new NavigateActionMethod,
+		"stop-action" -> new StopActionMethod,
+		"current-page-request" -> new CurrentPageRequestMethod
 	)
 
 	@OnWebSocketConnect def connected(session: Session): Unit = {
@@ -43,16 +49,19 @@ class WebSocketHandler {
 	}
 
 	@OnWebSocketError def onError(session: Session, error: Throwable): Unit = {
-		Logger.error(error)
+		if (!error.isInstanceOf[CloseException]) {
+			Logger.error(error)
+		}
 	}
 
 	def sendUpdate(message: String, jsonObject: JsonObject): Unit = {
 		jsonObject.addProperty("updateType", message)
 		val payload = WebSocketHandler.gson.toJson(jsonObject)
 
+		Logger.debug("Write to WebSocket: {0}", payload)
 		sessions.stream()
 			.filter(session => session.isOpen)
-			.forEach(session => session.getRemote.sendString(payload))
+			.forEach(session => session.getRemote.sendStringByFuture(payload))
 	}
 }
 
@@ -62,9 +71,12 @@ object WebSocketHandler {
 
 	private val gson = new Gson()
 
-	def sendResponse(session: Session, message: Message, response: JsonObject) = {
+	private def sendResponse(session: Session, message: Message, response: JsonObject): Unit = {
 		response.addProperty("messageId", message.messageId)
 		response.addProperty("type", message.`type`)
-		session.getRemote.sendString(gson.toJson(response))
+
+		val payload = gson.toJson(response)
+		Logger.debug("Write to WebSocket: {0}", payload)
+		session.getRemote.sendString(payload)
 	}
 }
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/MainWindowPageListener.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/MainWindowPageListener.scala
new file mode 100644
index 0000000000000000000000000000000000000000..fd1ceaefe77e5a54abf8a9fa161953a20da1c463
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/MainWindowPageListener.scala
@@ -0,0 +1,19 @@
+package de.tobias.playpad.plugin.api.websocket.listener
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.plugin.MainWindowListener
+import de.tobias.playpad.plugin.api.websocket.WebSocketHandler
+import de.tobias.playpad.viewcontroller.main.IMainViewController
+
+class MainWindowPageListener extends MainWindowListener {
+
+	override def onInit(t: IMainViewController): Unit = {
+	}
+
+	override def onCurrentPageChanged(newPage: Int): Unit = {
+		val payload = new JsonObject
+		payload.addProperty("newPage", newPage)
+
+		WebSocketHandler.instance.sendUpdate("current-page-changed", payload)
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadApiListener.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadApiListener.scala
new file mode 100644
index 0000000000000000000000000000000000000000..2efd7e127694e81ae1d90b06e742dd292bba8d26
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadApiListener.scala
@@ -0,0 +1,28 @@
+package de.tobias.playpad.plugin.api.websocket.listener
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.pad.{Pad, PadStatus}
+import de.tobias.playpad.plugin.PadListener
+import de.tobias.playpad.plugin.api.websocket.WebSocketHandler
+
+class PadApiListener extends PadListener {
+
+	override def onNameChanged(pad: Pad, oldValue: String, newValue: String): Unit = {
+		val payload = new JsonObject
+
+		payload.addProperty("pad", pad.getUuid.toString)
+		payload.addProperty("oldValue", oldValue)
+		payload.addProperty("newValue", newValue)
+
+		WebSocketHandler.instance.sendUpdate("pad-name-changed", payload)
+	}
+
+	override def onStatusChange(pad: Pad, oldValue: PadStatus, newValue: PadStatus): Unit = {
+		val payload = new JsonObject
+
+		payload.addProperty("pad", pad.getUuid.toString)
+		payload.addProperty("status", newValue.name())
+
+		WebSocketHandler.instance.sendUpdate("pad-status-changed", payload)
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadStatusListener.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadStatusListener.scala
deleted file mode 100644
index ffd31cb462573fa1630bcf9448d4f4eda0b7a387..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/listener/PadStatusListener.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package de.tobias.playpad.plugin.api.websocket.listener
-
-import com.google.gson.JsonObject
-import de.tobias.playpad.pad.{Pad, PadStatus}
-import de.tobias.playpad.plugin.PadListener
-import de.tobias.playpad.plugin.api.websocket.WebSocketHandler
-
-class PadStatusListener extends PadListener {
-
-	override def onStatusChange(pad: Pad, newValue: PadStatus): Unit = {
-		val payload = new JsonObject
-
-		payload.addProperty("pad", pad.getUuid.toString)
-		payload.addProperty("status", newValue.name())
-
-		WebSocketHandler.instance.sendUpdate("pad-status-changed", payload)
-	}
-}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CartActionMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CartActionMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..afddc207a9c952dc6eee11394a24411b770e87d5
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CartActionMethod.scala
@@ -0,0 +1,44 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.pad.PadStatus
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import de.tobias.playpad.project.page.PadIndex
+import javafx.application.Platform
+import org.eclipse.jetty.websocket.api.Session
+
+import java.util.UUID
+
+class CartActionMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val padId = if (message.payload.get("padId") != null) {
+			UUID.fromString(message.payload.get("padId").getAsString)
+		} else if (message.payload.get("padIndex") != null) {
+			val padIndex = message.payload.get("padIndex").getAsInt
+			val currentPage = PlayPadPlugin.getInstance().getMainViewController.getPage
+			val pad = PlayPadPlugin.getInstance().getCurrentProject.getPad(new PadIndex(padIndex, currentPage))
+			pad.getUuid
+		} else {
+			throw new IllegalArgumentException("Neither pad nor padIndex provided")
+		}
+
+		val currentProject = PlayPadPlugin.getInstance().getCurrentProject
+		val pad = currentProject.getPad(padId)
+
+		if (pad == null || pad.getStatus == PadStatus.EMPTY || pad.getStatus == PadStatus.ERROR) {
+			return null
+		}
+		Platform.runLater(() => {
+			if (pad.isPlay) {
+				pad.stop()
+			} else {
+				pad.play()
+			}
+		})
+
+		null
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CurrentPageRequestMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CurrentPageRequestMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..8e2a399af792d2baaf9cae1ccb003edb03396400
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/CurrentPageRequestMethod.scala
@@ -0,0 +1,16 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import org.eclipse.jetty.websocket.api.Session
+
+class CurrentPageRequestMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val response = new JsonObject()
+		response.addProperty("page", PlayPadPlugin.getInstance().getMainViewController.getPage)
+		response
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/NavigateActionMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/NavigateActionMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..e349be8bf4272ffc69caa11bf06d1ff06b8a2dcd
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/NavigateActionMethod.scala
@@ -0,0 +1,24 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import javafx.application.Platform
+import org.eclipse.jetty.websocket.api.Session
+
+class NavigateActionMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val mainViewController = PlayPadPlugin.getInstance.getMainViewController
+
+		message.payload.get("action").getAsString match {
+			case "PREVIOUS" =>
+				Platform.runLater(() => mainViewController.showPage(mainViewController.getPage - 1))
+			case "NEXT" =>
+				Platform.runLater(() => mainViewController.showPage(mainViewController.getPage + 1))
+			case _ =>
+		}
+		null
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PadStatusChangeMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PadStatusChangeMethod.scala
index 492583d000d44bdd997507e27fae325436c0b21c..e41f49e477dc4f4d7d9f0ab9a3f2f12e1fe78faa 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PadStatusChangeMethod.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PadStatusChangeMethod.scala
@@ -1,7 +1,5 @@
 package de.tobias.playpad.plugin.api.websocket.methods
 
-import java.util.UUID
-
 import com.google.gson.JsonObject
 import de.tobias.playpad.PlayPadPlugin
 import de.tobias.playpad.pad.PadStatus
@@ -10,6 +8,8 @@ import de.tobias.playpad.plugin.api.websocket.message.Message
 import javafx.application.Platform
 import org.eclipse.jetty.websocket.api.Session
 
+import java.util.UUID
+
 class PadStatusChangeMethod extends MethodExecutable {
 	override def execute(session: Session, message: Message): JsonObject = {
 		val padId = UUID.fromString(message.payload.get("pad").getAsString)
@@ -27,6 +27,6 @@ class PadStatusChangeMethod extends MethodExecutable {
 			}
 		})
 
-		new JsonObject
+		null
 	}
 }
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PageActionMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PageActionMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..09c89060903d98e410139b6acf3ddc771aae0196
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PageActionMethod.scala
@@ -0,0 +1,22 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import javafx.application.Platform
+import org.eclipse.jetty.websocket.api.Session
+
+class PageActionMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val project = PlayPadPlugin.getInstance.getCurrentProject
+		val mainViewController = PlayPadPlugin.getInstance.getMainViewController
+		val targetPage = message.payload.get("page").getAsInt
+
+		if (targetPage < 0 || targetPage >= project.getPages.size) return null
+
+		Platform.runLater(() => mainViewController.showPage(targetPage))
+		return null
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PingMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PingMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..62594789acbdbb10e1c48aa483977a1daf4fdc4f
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/PingMethod.scala
@@ -0,0 +1,15 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import org.eclipse.jetty.websocket.api.Session
+
+class PingMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val response = new JsonObject()
+		response.addProperty("pong", System.currentTimeMillis())
+		response
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/ProjectCurrentMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/ProjectCurrentMethod.scala
index ac5cd11148fc9d3fd31276b49bde39f5664fb631..36cd7a8dfe8522790ee58c872919d46f3fce7153 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/ProjectCurrentMethod.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/ProjectCurrentMethod.scala
@@ -13,7 +13,7 @@ class ProjectCurrentMethod extends MethodExecutable {
 		val currentProject = PlayPadPlugin.getInstance().getCurrentProject
 
 		if (currentProject == null) {
-			new JsonObject
+			null
 		} else {
 			ProjectSerializer.serializeProject(currentProject, Profile.currentProfile)
 		}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/StopActionMethod.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/StopActionMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..30622685415e50b83668b60b7774ff56778e5e26
--- /dev/null
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/methods/StopActionMethod.scala
@@ -0,0 +1,19 @@
+package de.tobias.playpad.plugin.api.websocket.methods
+
+import com.google.gson.JsonObject
+import de.tobias.playpad.PlayPadPlugin
+import de.tobias.playpad.pad.PadStatus
+import de.tobias.playpad.plugin.api.websocket.MethodExecutable
+import de.tobias.playpad.plugin.api.websocket.message.Message
+import org.eclipse.jetty.websocket.api.Session
+
+class StopActionMethod extends MethodExecutable {
+
+	override def execute(session: Session, message: Message): JsonObject = {
+		val project = PlayPadPlugin.getInstance.getCurrentProject
+		project.getPads.stream().forEach(pad => {
+			if ((pad.getStatus eq PadStatus.PLAY) || (pad.getStatus eq PadStatus.PAUSE)) pad.setStatus(PadStatus.STOP, true)
+		})
+		null
+	}
+}
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/serialize/ProjectSerializer.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/serialize/ProjectSerializer.scala
index 3c1e157137bf1ed7d405a784bdf87510581443a8..64951521461cd2d2eb2dd9d6da23a8baae77f9c8 100644
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/serialize/ProjectSerializer.scala
+++ b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/serialize/ProjectSerializer.scala
@@ -12,8 +12,6 @@ object ProjectSerializer {
 
 		result.addProperty("id", project.getProjectReference.getUuid.toString)
 		result.addProperty("name", project.getProjectReference.getName)
-		result.addProperty("columns", project.getSettings.getColumns)
-		result.addProperty("rows", project.getSettings.getRows)
 
 		val pageArray = new JsonArray()
 		project.getPages.forEach(page => {
@@ -34,8 +32,12 @@ object ProjectSerializer {
 				padObject.addProperty("page", pad.getPage.getPosition)
 
 				val padDesign = new JsonObject
-				padDesign.add("normal", serializeDesign(pad.getPadSettings.getDesign.getBackgroundColor))
-				padDesign.add("play", serializeDesign(pad.getPadSettings.getDesign.getPlayColor))
+				if (pad.getPadSettings.getDesign.isEnableCustomBackgroundColor) {
+					padDesign.add("normal", serializeDesign(pad.getPadSettings.getDesign.getBackgroundColor))
+				}
+				if (pad.getPadSettings.getDesign.isEnableCustomPlayColor) {
+					padDesign.add("play", serializeDesign(pad.getPadSettings.getDesign.getPlayColor))
+				}
 				padObject.add("design", padDesign)
 
 				padArray.add(padObject)
@@ -51,6 +53,11 @@ object ProjectSerializer {
 		globalDesign.add("play", serializeDesign(profile.getProfileSettings.getDesign.getPlayColor))
 		result.add("design", globalDesign)
 
+		val settings = new JsonObject
+		settings.addProperty("columns", project.getSettings.getColumns)
+		settings.addProperty("rows", project.getSettings.getRows)
+		result.add("settings", settings)
+
 		result
 	}
 
diff --git a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettingsViewController.scala b/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettingsViewController.scala
deleted file mode 100644
index 4f86d86df6e85433d7bd38a2dfb21fcb99361fdd..0000000000000000000000000000000000000000
--- a/PlayWallPlugins/PlayWallPluginWebAPI/src/main/scala/de/tobias/playpad/plugin/api/websocket/settings/WebApiSettingsViewController.scala
+++ /dev/null
@@ -1,81 +0,0 @@
-package de.tobias.playpad.plugin.api.websocket.settings
-
-import java.nio.file.Files
-
-import de.thecodelabs.logger.Logger
-import de.thecodelabs.storage.settings.{Storage, StorageTypes}
-import de.thecodelabs.utils.ui.scene.input.NumberTextField
-import de.thecodelabs.utils.util.Localization
-import de.tobias.playpad.plugin.api.WebApiPlugin
-import de.tobias.playpad.settings.GlobalSettings
-import de.tobias.playpad.viewcontroller.option.GlobalSettingsTabViewController
-import javafx.fxml.FXML
-import javafx.scene.control.CheckBox
-
-class WebApiSettingsViewController(val webApiSettings: WebApiSettings) extends GlobalSettingsTabViewController {
-
-	load("plugin/webapi/view", "WebApiSettings", Localization.getBundle)
-
-	@FXML
-	var activeCheckbox: CheckBox = _
-
-	@FXML
-	var portTextField: NumberTextField = _
-
-	/**
-	 * Lädt alle Einstellungen vom Model in die GUI.
-	 *
-	 * @param settings Aktuelles GlobalSettings
-	 */
-	override def loadSettings(settings: GlobalSettings): Unit = {
-		activeCheckbox.setSelected(webApiSettings.isEnabled)
-		portTextField.setText(webApiSettings.getPort.toString)
-	}
-
-	/**
-	 * Speichert alle Änderungen in das Model.
-	 *
-	 * @param settings Aktuelles GlobalSettings
-	 */
-	override def saveSettings(settings: GlobalSettings): Unit = {
-		webApiSettings.setEnabled(activeCheckbox.isSelected)
-		webApiSettings.setPort(portTextField.getText.toInt)
-
-		saveToFile()
-	}
-
-	/**
-	 * Gibt <code>true</code> zurück, wenn im Hauptprogramm etwas neu geladen werden muss.
-	 *
-	 * @return <code>true</code> Benötigt Reload
-	 */
-	override def needReload(): Boolean = {
-		false
-	}
-
-	/**
-	 * Prüft ob die eingetragen Einstellungen erlaubt sind. Bei falschen Eingaben können die Einstellungen nicht
-	 * geschlossen werden.
-	 *
-	 * @return <code>true</code> Einstellungen erlaubt. <code>false</code> Einstellungen fehlerhaft.
-	 */
-	override def validSettings(): Boolean = {
-		true
-	}
-
-	/**
-	 * Gibt den Namen für den Tab zurück.
-	 *
-	 * @return Display Name des Tabs.
-	 */
-	override def name(): String = Localization.getString("webapi.settings")
-
-	private def saveToFile(): Unit = {
-		val settingsPath = WebApiPlugin.getWebApiSettingsPath
-		if (Files.notExists(settingsPath)) {
-			Files.createDirectories(settingsPath.getParent)
-		}
-		Storage.save(StorageTypes.JSON, webApiSettings)
-		Logger.info(f"Save WebAPI settings to $settingsPath")
-	}
-}
diff --git a/PlayWallPlugins/pom.xml b/PlayWallPlugins/pom.xml
index 53ba5f57847adeeca8830e9ac4dd854bcd0c0426..8f43305aa69b95f3be3ed3a318e659d169b5e896 100644
--- a/PlayWallPlugins/pom.xml
+++ b/PlayWallPlugins/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>de.tobias.playpad</groupId>
         <artifactId>PlayWallDesktop</artifactId>
-        <version>7.1.0</version>
+        <version>7.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/pom.xml b/pom.xml
index 12df42938f36fa1d887b2280069c9e5f61dcf0a6..7f9b26a0f12eac5e16004304ee450e35f0e5de3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,11 +6,11 @@
 
     <groupId>de.tobias.playpad</groupId>
     <artifactId>PlayWallDesktop</artifactId>
-    <version>7.1.0</version>
+    <version>7.2.0</version>
     <packaging>pom</packaging>
 
     <properties>
-        <project.build.code>43</project.build.code>
+        <project.build.code>45</project.build.code>
         <project.versionDate>${maven.build.timestamp}</project.versionDate>
         <maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
 
@@ -30,13 +30,16 @@
         <itextpdf.version>5.5.13</itextpdf.version>
         <jackson-dataformat-csv.version>2.11.3</jackson-dataformat-csv.version>
 
-        <spark-core.version>[2.9.0,)</spark-core.version>
+        <spark-core.version>[2.9.3,)</spark-core.version>
+        <PlayWallWebApiClient.version>1.2.2</PlayWallWebApiClient.version>
 
         <jna.version>5.6.0</jna.version>
 
         <scala-library.version>2.13.3</scala-library.version>
         <junit.version>4.13.1</junit.version>
 
+        <jni4net.j.version>0.8.8.0</jni4net.j.version>
+
         <gson.version>2.8.6</gson.version>
         <json-smart.version>1.3.1</json-smart.version>
         <sqlite-jdbc.version>3.32.3.2</sqlite-jdbc.version>