diff --git a/src/main/java/de/tudresden/inf/st/mg/ImmersiveSortingController.java b/src/main/java/de/tudresden/inf/st/mg/ImmersiveSortingController.java
index d5efac843d5e4ff0f9d33d444218a8f83b95079d..7aea0bf42d121c5797842a4d4de963913778d379 100644
--- a/src/main/java/de/tudresden/inf/st/mg/ImmersiveSortingController.java
+++ b/src/main/java/de/tudresden/inf/st/mg/ImmersiveSortingController.java
@@ -6,13 +6,11 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import de.tudresden.inf.st.mg.common.MotionGrammarConfig;
 import de.tudresden.inf.st.mg.common.MotionGrammarParser;
 import de.tudresden.inf.st.mg.common.TableDeserializer;
+import de.tudresden.inf.st.mg.common.Webserver;
 import de.tudresden.inf.st.mg.jastadd.model.JastAddList;
 import de.tudresden.inf.st.mg.jastadd.model.RobotWorld;
 import de.tudresden.inf.st.mg.jastadd.model.Table;
 import de.tudresden.inf.st.mg.jastadd.model.World;
-import io.javalin.Javalin;
-import io.javalin.core.JavalinConfig;
-import io.javalin.websocket.WsContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -21,25 +19,15 @@ import java.io.IOException;
 import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.time.Instant;
 import java.util.Comparator;
-import java.util.Date;
-import java.util.Map;
 import java.util.Random;
-import java.util.concurrent.ConcurrentHashMap;
 
 public class ImmersiveSortingController {
 
   public static final Path TIDY_AST_DIAGRAM_DIR = Path.of("src", "gen", "resources", "diagrams", "parsing", "tidy");
   private static final Logger logger = LoggerFactory.getLogger(ImmersiveSortingController.class);
-  private static final Map<WsContext, String> clients = new ConcurrentHashMap<>();
-  private static ImmersiveSortingController INSTANCE;
   private final RobotWorld world;
   private ParseThread parse;
-  private Diagram ast;
-  private Diagram context;
-
-  private boolean initialized = false;
 
   public ImmersiveSortingController(boolean simulate) {
     MotionGrammarConfig.astDiagramDir = TIDY_AST_DIAGRAM_DIR;
@@ -64,9 +52,19 @@ public class ImmersiveSortingController {
       e.printStackTrace();
     }
 
-    initializeWebserver();
+    // initialize webserver
+    Webserver ws = Webserver.getInstance();
+
+    ws.setCallback("cell1", (String command) -> {
+      logger.info("got message " + command);
+      if ("parse".equals(command)) {
+        startParse();
+      } else if ("reset".equals(command)) {
+        resetSelections();
+        printContext("reset selection");
+      }
+    });
 
-    initialized = true;
 
     World.enableContextPrinting(true);
     world.printContext("initial");
@@ -107,20 +105,13 @@ public class ImmersiveSortingController {
    * on localhost:1883
    */
   public static void main(String[] args) {
-    ImmersiveSortingController controller = ImmersiveSortingController.getInstance();
+    ImmersiveSortingController controller = new ImmersiveSortingController(false);
     try {
       Thread.sleep(Long.MAX_VALUE);
     } catch (InterruptedException ignored) {
     }
   }
 
-  public static ImmersiveSortingController getInstance() {
-    if (INSTANCE == null) {
-      INSTANCE = new ImmersiveSortingController(false);
-    }
-    return INSTANCE;
-  }
-
   public void startParse() {
     if (parse != null && parse.isAlive()) {
       parse.stopParsing();
@@ -141,79 +132,6 @@ public class ImmersiveSortingController {
     world.printContext(message);
   }
 
-  private void initializeWebserver() {
-    ast = new Diagram(new Date(), 0, "<no rule>", "", "ast");
-    context = new Diagram(Date.from(Instant.now()), 0, "<no rule>", "", "context");
-
-    Javalin webserver = Javalin.create(JavalinConfig::enableCorsForAllOrigins).start(7070);
-    webserver.get("/", ctx -> ctx.result("the context is delivered at /context and the ast is delivered at /ast"));
-
-    webserver.get("/ast/", ctx -> {
-      ctx.json(ast);
-    });
-
-    webserver.get("/context/", ctx -> {
-      ctx.json(context);
-    });
-
-    webserver.ws("ast-events", ws -> {
-      ws.onConnect(ctx -> {
-        System.err.println("somebody connected " + ctx.host());
-        clients.put(ctx, ctx.host());
-      });
-      ws.onClose(ctx -> {
-        System.err.println("somebody disconnected");
-        clients.remove(ctx);
-      });
-      ws.onMessage(ctx -> {
-        if (initialized) {
-          logger.info("got message " + ctx.message());
-        } else {
-          logger.error("ignoring message " + ctx.message() + ", because system is not initialized yet");
-          return;
-        }
-        if ("parse".equals(ctx.message())) {
-          startParse();
-        } else if ("reset".equals(ctx.message())) {
-          resetSelections();
-          printContext("reset selection");
-        }
-      });
-    });
-  }
-
-  public void publish(Date timestamp, int step, String parseRule, Path diagramPath, String type) {
-    try {
-      if ("ast".equals(type)) {
-        ast = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
-        clients.keySet().forEach(session -> session.send(ast));
-      } else if ("context".equals(type)) {
-        context = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
-        clients.keySet().forEach(session -> session.send(context));
-      }
-    } catch (IOException e) {
-      System.err.println("Unable to read " + type + " diagram file " + diagramPath);
-      e.printStackTrace();
-    }
-  }
-
-  public static class Diagram {
-    public Date timestamp;
-    public int step;
-    public String parseRule;
-    public String diagram;
-
-    public String type;
-
-    public Diagram(Date timestamp, int step, String parseRule, String diagram, String type) {
-      this.timestamp = timestamp;
-      this.step = step;
-      this.parseRule = parseRule;
-      this.diagram = diagram;
-      this.type = type;
-    }
-  }
-
   class ParseThread extends Thread {
 
     private RobotParser parser;
diff --git a/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java b/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
index 0732e15e0df5aa79945dcd947e820898da6b767f..def2e40de27b8518fa40afc9be377ca7b3751a6f 100644
--- a/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
+++ b/src/main/java/de/tudresden/inf/st/mg/common/MotionGrammarParser.java
@@ -3,7 +3,6 @@ package de.tudresden.inf.st.mg.common;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamBooleanSetting;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamStringSetting;
-import de.tudresden.inf.st.mg.ImmersiveSortingController;
 import de.tudresden.inf.st.mg.jastadd.model.*;
 
 import java.io.IOException;
@@ -34,7 +33,7 @@ public abstract class MotionGrammarParser<T extends MotionGrammarElement> {
     }
     try {
       TimeUnit.MILLISECONDS.sleep(100);
-    } catch(InterruptedException e) {
+    } catch (InterruptedException ignored) {
     }
   }
 
@@ -84,7 +83,7 @@ public abstract class MotionGrammarParser<T extends MotionGrammarElement> {
           .skinParam(SkinParamBooleanSetting.Shadowing, false)
           .skinParam(SkinParamStringSetting.backgroundColor, "white")
           .dumpAsSVG(svgPath);
-      ImmersiveSortingController.getInstance().publish(new Date(), timeStep_, step, svgPath, "ast");
+      Webserver.getInstance().publish(new Date(), timeStep_, step, svgPath, "ast");
       timeStep_++;
     } catch (IOException e) {
       e.printStackTrace();
diff --git a/src/main/java/de/tudresden/inf/st/mg/common/Webserver.java b/src/main/java/de/tudresden/inf/st/mg/common/Webserver.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fdc27b47da327fd6c7bd7b1d73461c6ca033410
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/mg/common/Webserver.java
@@ -0,0 +1,110 @@
+package de.tudresden.inf.st.mg.common;
+
+import io.javalin.Javalin;
+import io.javalin.core.JavalinConfig;
+import io.javalin.websocket.WsContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+public class Webserver {
+
+  private static final Logger logger = LoggerFactory.getLogger(Webserver.class);
+  private final Map<WsContext, String> clients = new ConcurrentHashMap<>();
+  private static Webserver INSTANCE;
+  private Diagram ast;
+  private Diagram context;
+
+  private final Map<String, Consumer<String>> callbacks = new ConcurrentHashMap<>();
+
+  private Webserver() {
+    ast = new Diagram(new Date(), 0, "<no rule>", "", "ast");
+    context = new Diagram(Date.from(Instant.now()), 0, "<no rule>", "", "context");
+
+    Javalin webserver = Javalin.create(JavalinConfig::enableCorsForAllOrigins).start(7070);
+    webserver.get("/", ctx -> ctx.result("the context is delivered at /context and the ast is delivered at /ast"));
+
+    webserver.get("/ast/", ctx -> {
+      ctx.json(ast);
+    });
+
+    webserver.get("/context/", ctx -> {
+      ctx.json(context);
+    });
+
+    webserver.ws("ast-events", ws -> {
+      ws.onConnect(ctx -> {
+        System.err.println("somebody connected " + ctx.host());
+        clients.put(ctx, ctx.host());
+      });
+      ws.onClose(ctx -> {
+        System.err.println("somebody disconnected");
+        clients.remove(ctx);
+      });
+      ws.onMessage(ctx -> {
+        String[] splitString = ctx.message().split("\\s");
+        if (splitString.length == 2) {
+          if (callbacks.containsKey(splitString[0])) {
+            callbacks.get(splitString[0]).accept(splitString[1]);
+          }
+        } else {
+          logger.error("Unable to execute command, because it does not fit the command syntax: " + ctx.message());
+        }
+      });
+    });
+
+  }
+
+  public static Webserver getInstance() {
+    if (INSTANCE == null) {
+      INSTANCE = new Webserver();
+    }
+    return INSTANCE;
+  }
+
+  public void setCallback(String client, Consumer<String> callback) {
+    callbacks.put(client, callback);
+  }
+
+  public void publish(Date timestamp, int step, String parseRule, Path diagramPath, String type) {
+    try {
+      if ("ast".equals(type)) {
+        ast = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
+        clients.keySet().forEach(session -> session.send(ast));
+      } else if ("context".equals(type)) {
+        context = new Diagram(timestamp, step, parseRule, Files.readString(diagramPath), type);
+        clients.keySet().forEach(session -> session.send(context));
+      }
+    } catch (IOException e) {
+      System.err.println("Unable to read " + type + " diagram file " + diagramPath);
+      e.printStackTrace();
+    }
+  }
+
+
+  public static class Diagram {
+    public Date timestamp;
+    public int step;
+    public String parseRule;
+    public String diagram;
+
+    public String type;
+
+    public Diagram(Date timestamp, int step, String parseRule, String diagram, String type) {
+      this.timestamp = timestamp;
+      this.step = step;
+      this.parseRule = parseRule;
+      this.diagram = diagram;
+      this.type = type;
+    }
+  }
+
+}