diff --git a/build.gradle b/build.gradle
index d893e1b17132278a8c1da641ed97cfb7e4511b16..981d28aa74ce1fdfca6d6d5cdb7e7ebf5ab06546 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,6 +41,11 @@ dependencies {
     testImplementation group: 'org.assertj', name: 'assertj-core', version: "${assertj_version}"
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupyter_version}"
     testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupyter_version}"
+    implementation group: 'io.javalin', name: 'javalin', version: "${javalin_version}"
+    implementation group: 'org.json', name: 'json', version: '20220320'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.3'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.13.3'
+    implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.31'
 }
 
 def versionFile = 'src/main/resources/Version.properties'
diff --git a/gradle.properties b/gradle.properties
index 8a37e6b4108d36221fa3ca9fc2094a414f81289d..ccc134c3c611385222c9866a1016ba6b162994fb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,3 +4,4 @@ jupyter_version = 5.8.2
 assertj_version = 3.22.0
 grammar2uml_version = 0.2.1
 jastaddgradle_version = 1.14.5
+javalin_version = 4.6.3
diff --git a/src/main/java/de/tudresden/inf/st/mg/common/DiagramProvider.java b/src/main/java/de/tudresden/inf/st/mg/common/DiagramProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..eaad995a53701c519fb54bf545a390671c1b7196
--- /dev/null
+++ b/src/main/java/de/tudresden/inf/st/mg/common/DiagramProvider.java
@@ -0,0 +1,93 @@
+package de.tudresden.inf.st.mg.common;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.tudresden.inf.st.mg.common.DiagramProvider.AST;
+import io.javalin.Javalin;
+import io.javalin.http.ContentType;
+import io.javalin.websocket.WsContext;
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DiagramProvider {
+
+  private static DiagramProvider INSTANCE;
+
+  private static Map<WsContext, String> clients = new ConcurrentHashMap<>();
+
+  public static DiagramProvider getInstance() {
+    if (INSTANCE == null) {
+      INSTANCE = new DiagramProvider();
+    }
+    return INSTANCE;
+  }
+
+  private List<AST> asts;
+
+  private DiagramProvider() {
+    asts = new ArrayList<>();
+
+    Javalin app = Javalin.create().start(7070);
+    app.get("/", ctx -> ctx.result("Hello World"));
+
+    app.get("/asts/", ctx -> {
+      ctx.json(asts);
+    });
+
+    app.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 -> {
+        System.err.println("somebody sent us a message: " + ctx.message());
+      });
+    });
+  }
+
+  public void publishAst(Date timestamp, int step, String parseRule, Path diagramPath) {
+    System.err.println("publishing AST");
+    try {
+      asts.add(new AST(timestamp, step, parseRule, Files.readString(diagramPath)));
+      clients.keySet().forEach(session -> {
+        System.err.println("sending");
+        session.send(asts);
+      });
+    } catch (IOException e) {
+      System.err.println("Unable to read AST diagram file " + diagramPath);
+      e.printStackTrace();
+    }
+  }
+
+  public void publishContext() {
+
+  }
+
+  public static class AST {
+    public Date timestamp;
+    public int step;
+    public String parseRule;
+    public String diagram;
+
+    public AST(Date timestamp, int step, String parseRule, String diagram) {
+      this.timestamp = timestamp;
+      this.step = step;
+      this.parseRule = parseRule;
+      this.diagram = diagram;
+    }
+  }
+
+}
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 3777975ee71ac96e5cb48cbc0582a56d0ce460cb..9eb57c6bc30e97ca331b6e7f15ec8971127a3040 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
@@ -9,6 +9,7 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -41,7 +42,7 @@ public abstract class MotionGrammarParser<T extends MotionGrammarElement> {
     final String H_T = "F2D8AC";
 
     try {
-
+      Path svgPath = MotionGrammarConfig.astDiagramDir.resolve("AST-" + String.format("%03d", timeStep_) + "-" + new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss.SSS").format(new java.util.Date()) + "-" + step + ".svg");
       Dumper.read(rootContainer_.getChild(0))
           .setBackgroundColorMethod(n -> (n == highlightNode ? (n instanceof Token ? H_T : H_N) : (n instanceof Token ? N_T : N_N)))
           .includeChildWhen((parentNode, childNode, contextName) -> !(parentNode instanceof Token))
@@ -64,7 +65,9 @@ public abstract class MotionGrammarParser<T extends MotionGrammarElement> {
           .customPreamble("title " + step)
           .skinParam(SkinParamBooleanSetting.Shadowing, false)
           .skinParam(SkinParamStringSetting.backgroundColor, "white")
+          .dumpAsSVG(svgPath)
           .dumpAsPNG(MotionGrammarConfig.astDiagramDir.resolve("AST-" + String.format("%03d", timeStep_) + "-" + new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss.SSS").format(new java.util.Date()) + "-" + step + ".png"));
+      DiagramProvider.getInstance().publishAst(new Date(), timeStep_, step, svgPath);
       timeStep_++;
     } catch (IOException e) {
       e.printStackTrace();
diff --git a/src/test/java/de/tudresden/inf/st/mg/ParserTest.java b/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
index 3950d514953d3a6a8970a02b50388bc0ccb7e5bf..378e0d343c7b815e4af8ff91c5df0d774cb6fbcd 100644
--- a/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
+++ b/src/test/java/de/tudresden/inf/st/mg/ParserTest.java
@@ -3,6 +3,7 @@ package de.tudresden.inf.st.mg;
 import de.tudresden.inf.st.mg.common.MotionGrammarParser;
 import de.tudresden.inf.st.mg.jastadd.model.*;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 import java.io.File;
@@ -36,6 +37,7 @@ public class ParserTest {
   }
 
   @Test
+  @Disabled
   void runLodUnloadParser() throws MotionGrammarParser.ParseException {
 
     // for some reason, the best random seed value here is 1 and not 0???
@@ -63,6 +65,11 @@ public class ParserTest {
     var result = parser.parse();
 
     assertThat(result).isNotNull().isInstanceOf(Tidy.class);
+    try {
+      Thread.sleep(100000*1000);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
   }
 
 }