diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/DecayPhaseShareBasedFilter.java b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/DecayPhaseShareBasedFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..c40d467f440ea3b57f9af1e0d1ecc19a9af5fab4
--- /dev/null
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/DecayPhaseShareBasedFilter.java
@@ -0,0 +1,121 @@
+package de.uni_bremen.agst.mimesis.filter;
+
+import de.uni_bremen.agst.mimesis.persistence.events.*;
+import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractPhaseShareBasedProbabilityVisitor;
+
+import java.util.*;
+
+/**
+ * Filter which uses predetermined probabilities for each event to be in a given phase to calculate the phase probabilities.
+ *
+ * This filter works similarly to the SimplePhaseShareBasedFilter, but instead of only considering the last 100 events
+ * the values are just being added up to a continuous score which is multiplied by a decay factor each time a new event is added.
+ *
+ * Also, some event types are simply ignored, because these events seem to not have occured often enough to make a meaningful
+ * guess as to which phase they belong to.
+ */
+public class DecayPhaseShareBasedFilter extends ThreePhaseFiltering {
+
+	/**
+	 * Visitor responsible for extracting probabilities for given events.
+	 */
+	private final ExtractPhaseShareBasedProbabilityVisitor extractor;
+
+	/**
+	 * The previous state of the event buffer. Used to determine if the event buffer has changed.
+	 */
+	private Deque<Event> investigationBuffer;
+
+	private Deque<Event> editingBuffer;
+
+	private Deque<Event> verificationBuffer;
+
+	private double investigationProbabilitySum = 0;
+
+	private double editingProbabilitySum = 0;
+
+	private double verificationProbabilitySum = 0;
+
+	private static final double INVESTIGATION_DECAY_FACTOR = 0.99;
+
+	private static final double EDITING_DECAY_FACTOR = 0.99;
+
+	private static final double VERIFICATION_DECAY_FACTOR = 0.99;
+
+	private Set<Class<? extends Event>> ignoredEventTypes = new HashSet<Class<? extends Event>>();
+
+	public DecayPhaseShareBasedFilter() {
+		super();
+
+		ignoredEventTypes.add(TreeViewerEvent.class);
+		ignoredEventTypes.add(TreeSelectionEvent.class);
+		ignoredEventTypes.add(EditorTextCursorEvent.class);
+		ignoredEventTypes.add(DebugEvent.class);
+		ignoredEventTypes.add(RecordingEvent.class);
+		ignoredEventTypes.add(EditorMouseEvent.class);
+
+		extractor = new ExtractPhaseShareBasedProbabilityVisitor();
+		investigationBuffer = new ArrayDeque<>(100);
+		editingBuffer = new ArrayDeque<>(100);
+		verificationBuffer = new ArrayDeque<>(100);
+	}
+
+	@Override
+	public void handleEvent(Event event) {
+		if(!ignoredEventTypes.contains(event.getClass())) {
+			decay();
+			super.handleEvent(event);
+		}
+	}
+
+	private void decay() {
+		investigationProbabilitySum *= INVESTIGATION_DECAY_FACTOR;
+		editingProbabilitySum *= EDITING_DECAY_FACTOR;
+		verificationProbabilitySum *= VERIFICATION_DECAY_FACTOR;
+	}
+
+	@Override
+	public void handleEvents(Collection<Event> events) {
+		for(Event event : events) {
+			if(!ignoredEventTypes.contains(event.getClass())) {
+				handleEvent(event);
+			}
+		}
+	}
+
+	@Override
+	protected double getInvestigationProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(investigationBuffer.peekFirst())) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getInvestigationFrequencyScore() != 0) {
+				investigationProbabilitySum += extractor.getInvestigationProbability();
+				investigationBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return investigationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getEditingProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(editingBuffer.peekFirst())) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getEditingFrequencyScore() != 0) {
+				editingProbabilitySum += extractor.getEditingProbability();
+				editingBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return editingProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getVerificationProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(verificationBuffer.peekFirst())) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getVerificationFrequencyScore() != 0) {
+				verificationProbabilitySum += extractor.getVerificationProbability();
+				verificationBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return verificationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+}
diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/FrequencyAccountedPhaseShareBasedFilter.java b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/FrequencyAccountedPhaseShareBasedFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..70f5e105e961897c260a5407544ed662c4420d6b
--- /dev/null
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/FrequencyAccountedPhaseShareBasedFilter.java
@@ -0,0 +1,128 @@
+package de.uni_bremen.agst.mimesis.filter;
+
+import de.uni_bremen.agst.mimesis.persistence.events.EditorMouseEvent;
+import de.uni_bremen.agst.mimesis.persistence.events.Event;
+import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractPhaseShareBasedProbabilityVisitor;
+import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractProbabilityVisitor;
+
+import java.time.Duration;
+import java.util.*;
+
+/**
+ * Filter which uses predetermined probabilities for each event to be in a given phase to calculate the phase probabilities.
+ *
+ * This filter works similarly to the SimplePhaseShareBasedFilter, but uses weights for the different event types based
+ * on their frequency in a certain phase. The aim of this method is to account for the fact that some events are inherently
+ * more common (like ScrollEvents) and therefore have more influence on the resulting value. This weighing must happen
+ * on the phase-level, not on the global level, because otherwise the filter is inherently biased towards shorter phases.
+ *
+ * Another difference is that the number of events considered is based on their weights and changes dynamically (and
+ * therefore also differs from phase to phase.
+ */
+public class FrequencyAccountedPhaseShareBasedFilter extends ThreePhaseFiltering {
+
+	/**
+	 * Visitor responsible for extracting probabilities for given events.
+	 */
+	private final ExtractPhaseShareBasedProbabilityVisitor extractor;
+
+	/**
+	 * The previous state of the event buffer. Used to determine if the event buffer has changed.
+	 */
+	private Deque<Event> investigationBuffer;
+
+	private Deque<Event> editingBuffer;
+
+	private Deque<Event> verificationBuffer;
+
+	private double investigationProbabilitySum = 0;
+
+	private double editingProbabilitySum = 0;
+
+	private double verificationProbabilitySum = 0;
+
+	private double investigationFrequencyScoreSum = 0;
+
+	private double editingFrequencyScoreSum = 0;
+
+	private double verificationFrequencyScoreSum = 0;
+
+	private static final double bufferLimit = 100d/3d;
+
+	public FrequencyAccountedPhaseShareBasedFilter() {
+		super();
+		extractor = new ExtractPhaseShareBasedProbabilityVisitor();
+		investigationBuffer = new ArrayDeque<>(100);
+		editingBuffer = new ArrayDeque<>(100);
+		verificationBuffer = new ArrayDeque<>(100);
+	}
+
+	@Override
+	public void handleEvent(Event event) {
+		limitBuffers();
+		super.handleEvent(event);
+	}
+
+	private void limitBuffers() {
+		while(investigationFrequencyScoreSum > bufferLimit && !investigationBuffer.isEmpty()) {
+			investigationBuffer.pollLast().accept(extractor);
+			investigationProbabilitySum -= extractor.getInvestigationProbability()/extractor.getInvestigationFrequencyScore();
+			investigationFrequencyScoreSum -= extractor.getInvestigationFrequencyScore();
+		}
+		while(editingFrequencyScoreSum > bufferLimit && !editingBuffer.isEmpty()) {
+			editingBuffer.pollLast().accept(extractor);
+			editingProbabilitySum -= extractor.getEditingProbability()/extractor.getEditingFrequencyScore();
+			editingFrequencyScoreSum -= extractor.getEditingFrequencyScore();
+		}
+		while(verificationFrequencyScoreSum > bufferLimit && !verificationBuffer.isEmpty()) {
+			verificationBuffer.pollLast().accept(extractor);
+			verificationProbabilitySum -= extractor.getVerificationProbability()/extractor.getVerificationFrequencyScore();
+			verificationFrequencyScoreSum -= extractor.getVerificationFrequencyScore();
+		}
+	}
+
+	@Override
+	public void handleEvents(Collection<Event> events) {
+		limitBuffers();
+		super.handleEvents(events);
+	}
+
+	@Override
+	protected double getInvestigationProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(investigationBuffer.peekFirst()) && !(events.peekFirst() instanceof EditorMouseEvent)) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getInvestigationFrequencyScore() != 0) {
+				investigationProbabilitySum += extractor.getInvestigationProbability()/extractor.getInvestigationFrequencyScore();
+				investigationFrequencyScoreSum += extractor.getInvestigationFrequencyScore();
+				investigationBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return investigationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getEditingProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(editingBuffer.peekFirst()) && !(events.peekFirst() instanceof EditorMouseEvent)) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getEditingFrequencyScore() != 0) {
+				editingProbabilitySum += extractor.getEditingProbability()/extractor.getEditingFrequencyScore();
+				editingFrequencyScoreSum += extractor.getEditingFrequencyScore();
+				editingBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return editingProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getVerificationProbability(Deque<Event> events, double currentProbability)  {
+		if(!events.isEmpty() && !events.peekFirst().equals(verificationBuffer.peekFirst()) && !(events.peekFirst() instanceof EditorMouseEvent)) {
+			events.peekFirst().accept(extractor);
+			if(extractor.getVerificationFrequencyScore() != 0) {
+				verificationProbabilitySum += extractor.getVerificationProbability()/extractor.getVerificationFrequencyScore();
+				verificationFrequencyScoreSum += extractor.getVerificationFrequencyScore();
+				verificationBuffer.addFirst(events.peekFirst());
+			}
+		}
+		return verificationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+}
diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/Phase.java b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/Phase.java
index cc8995b5f2015afe018424db45a6933bac354a22..2cda0cc48d73f7d7796089f47e26f40b6004ef10 100644
--- a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/Phase.java
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/Phase.java
@@ -27,12 +27,12 @@ class Phase {
 
 	/**
 	 * A function which takes in the buffer of recent events with the most recent event at the start as well as
-	 * the duration between when the last event happened until this event happened,
-	 * and returns a double with which the current probability will be multiplied.
+	 * the current probability
+	 * and returns a double with which the current probability will be replaced.
 	 * This will be applied every time a new Event occurs.
 	 * For the first event, the buffer will be empty and the duration will be zero.
 	 */
-	private final ToDoubleBiFunction<Deque<Event>, Duration> modifierFunction;
+	private final ToDoubleBiFunction<Deque<Event>, Double> modifierFunction;
 
 	/**
 	 * Constructs a new Phase with the given modifier function
@@ -40,22 +40,17 @@ class Phase {
 	 *                         that the user is currently in this phase.
 	 * @param phaseName the name for this phase.
 	 */
-	public Phase(ToDoubleBiFunction<Deque<Event>, Duration> modifierFunction, String phaseName) {
+	public Phase(ToDoubleBiFunction<Deque<Event>, Double> modifierFunction, String phaseName) {
 		this.modifierFunction = modifierFunction;
 		this.phaseName = phaseName;
 	}
 
 	/**
 	 * A function which takes in the buffer of recent events with the most recent event at the start,
-	 * and returns a double with which the current probability will be multiplied.
+	 * and replaced the current probability with a value calculated using the modifierFunction.
 	 * This will be applied every time a new Event occurs, so performance has to be taken into account.
 	 */
 	public void calculateModifier(Deque<Event> events) {
-		Duration duration = Duration.ZERO;
-		if (events.size() > 1) {
-			Iterator<Event> iter = events.iterator();
-			duration = Duration.ofMillis(iter.next().getTimestamp() - iter.next().getTimestamp()).abs();
-		}
-		probability *= modifierFunction.applyAsDouble(events, duration);
+		probability = modifierFunction.applyAsDouble(events, probability);
 	}
 }
diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/SimplePhaseShareBasedFilter.java b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/SimplePhaseShareBasedFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..828af61e77b1f25d9da11afb2ac1addf9bff6c2c
--- /dev/null
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/SimplePhaseShareBasedFilter.java
@@ -0,0 +1,95 @@
+package de.uni_bremen.agst.mimesis.filter;
+
+import de.uni_bremen.agst.mimesis.persistence.events.EditorMouseEvent;
+import de.uni_bremen.agst.mimesis.persistence.events.Event;
+import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractPhaseShareBasedProbabilityVisitor;
+import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractProbabilityVisitor;
+
+import java.time.Duration;
+import java.util.*;
+
+/**
+ * Filter which uses predetermined probabilities for each event to be in a given phase to calculate the phase probabilities.
+ *
+ * This filter calculates the phase probabilities based on the last 100 events. For each event/phase combination a
+ * probability has been provided, which has been determined using previous recordings. These probabilities are then
+ * summed up for the 100 considered events to determine a score for each phase. The probability is then divided by the
+ * sum of all three scores combined.
+ *
+ * This filter, even though it is very simple, manages to differentiate between investigation and editing phase pretty
+ * well, but has problems in finding verification phases.
+ *
+ * The probabilities used for this filter have not been calculated by just
+ * using the raw number of events of a given type divided by the total number of events of this type, because then there
+ * would be a heavy bias towards the editing phase (which is by far the longest phase). Instead, the values has been
+ * calculated by first determining the procentual share a given event type has in a given phase, and then dividing that
+ * by the sum of the procentual share of the given event type for all three phases.
+ */
+public class SimplePhaseShareBasedFilter extends ThreePhaseFiltering {
+
+	/**
+	 * Visitor responsible for extracting probabilities for given events.
+	 */
+	private final ExtractPhaseShareBasedProbabilityVisitor extractor;
+
+	/**
+	 * The previous state of the event buffer. Used to determine if the event buffer has changed.
+	 */
+	private Deque<Event> previousEvents;
+
+	private double investigationProbabilitySum = 0;
+
+	private double editingProbabilitySum = 0;
+
+	private double verificationProbabilitySum = 0;
+
+	public SimplePhaseShareBasedFilter() {
+		super();
+		extractor = new ExtractPhaseShareBasedProbabilityVisitor();
+		previousEvents = new ArrayDeque<>(100);
+	}
+
+	/**
+	 * Applies the extractor to the most recent event and sets necessary attributes.
+	 * After this method is executed, each probability can be accessed.
+	 * If the event queue did not change, the extractor will not be called again.
+	 * @param events the recent events
+	 */
+	private void extractProbability(Deque<Event> events) {
+		if (!events.peekFirst().equals(previousEvents.peekFirst())) {
+			if(!(events.peekFirst() instanceof EditorMouseEvent)) {
+				if(previousEvents.size() == 100) {
+					previousEvents.pollLast().accept(extractor);
+					investigationProbabilitySum -= extractor.getInvestigationProbability();
+					editingProbabilitySum -= extractor.getEditingProbability();
+					verificationProbabilitySum -= extractor.getVerificationProbability();
+				}
+				if (!events.isEmpty()) {
+					events.peekFirst().accept(extractor);
+					investigationProbabilitySum += extractor.getInvestigationProbability();
+					editingProbabilitySum += extractor.getEditingProbability();
+					verificationProbabilitySum += extractor.getVerificationProbability();
+				}
+				this.previousEvents.addFirst(events.peekFirst());
+			}
+		}
+	}
+
+	@Override
+	protected double getInvestigationProbability(Deque<Event> events, double currentProbability)  {
+		extractProbability(events);
+		return investigationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getEditingProbability(Deque<Event> events, double currentProbability)  {
+		extractProbability(events);
+		return editingProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+
+	@Override
+	protected double getVerificationProbability(Deque<Event> events, double currentProbability)  {
+		extractProbability(events);
+		return verificationProbabilitySum/(investigationProbabilitySum+editingProbabilitySum+verificationProbabilitySum);
+	}
+}
diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/ThreePhaseFiltering.java b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/ThreePhaseFiltering.java
index 646399fd9ea6fae4616a63cba75d38080eb30897..b50356f9ae96c886d20f7b721f81557b38c72e10 100644
--- a/common/src/main/java/de/uni_bremen/agst/mimesis/filter/ThreePhaseFiltering.java
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/filter/ThreePhaseFiltering.java
@@ -4,10 +4,7 @@ import de.uni_bremen.agst.mimesis.persistence.events.Event;
 import de.uni_bremen.agst.mimesis.persistence.events.visitor.ExtractProbabilityVisitor;
 
 import java.time.Duration;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Responsible for filtering incoming events using the "Three Phases" model,
@@ -18,28 +15,17 @@ import java.util.Map;
  *     <li>"Verification" phase</li>
  * </ol>
  */
-public class ThreePhaseFiltering {
+public abstract class ThreePhaseFiltering {
 
 	/**
 	 * The event filter responsible for calculating the probabilities implementing the three phase model.
 	 */
-	private final EventFilter threePhaseFilter;
+	protected final EventFilter threePhaseFilter;
 
-	/**
-	 * Visitor responsible for extracting probabilities for given events.
-	 */
-	private final ExtractProbabilityVisitor extractor;
-
-	/**
-	 * The previous state of the event buffer. Used to determine if the event buffer has changed.
-	 */
-	private Deque<Event> previousEvents;
-
-	public ThreePhaseFiltering() {
-		extractor = new ExtractProbabilityVisitor();
-		Phase investigation = new Phase(this::getInvestigationModifier, "Investigation");
-		Phase editing = new Phase(this::getEditingModifier, "Editing");
-		Phase verification = new Phase(this::getVerificationModifier, "Verification");
+	protected ThreePhaseFiltering() {
+		Phase investigation = new Phase(this::getInvestigationProbability, "Investigation");
+		Phase editing = new Phase(this::getEditingProbability, "Editing");
+		Phase verification = new Phase(this::getVerificationProbability, "Verification");
 
 		this.threePhaseFilter = new EventFilter(investigation, editing, verification);
 	}
@@ -52,6 +38,14 @@ public class ThreePhaseFiltering {
 		return threePhaseFilter.getAllProbabilities();
 	}
 
+	/**
+	 * Returns the phase that this filter currently predicts to be the most likely one.
+	 * @return the phase that this filter currently predicts to be the most likely one.
+	 */
+	public String getMostLikelyPhaseName() {
+		return threePhaseFilter.getMostLikelyPhase().getPhaseName();
+	}
+
 	/**
 	 * Recalculates the probabilities based on the new event.
 	 * @param event the new event
@@ -69,36 +63,9 @@ public class ThreePhaseFiltering {
 		threePhaseFilter.handleEvents(events);
 	}
 
-	/**
-	 * Applies the extractor to the most recent event and sets necessary attributes.
-	 * After this method is executed, each probability can be accessed.
-	 * If the event queue did not change, the extractor will not be called again.
-	 * @param events the recent events
-	 * @param duration the duration between the second-to-last and the last event
-	 */
-	private void extractProbability(Deque<Event> events, Duration duration) {
-		if (!events.equals(previousEvents)) {
-			extractor.setRecentEvents(events);
-			extractor.setSinceLastEvent(duration);
-			if (!events.isEmpty()) {
-				events.peekFirst().accept(extractor);
-			}
-			this.previousEvents = new ArrayDeque<>(events);
-		}
-	}
-
-	private double getInvestigationModifier(Deque<Event> events, Duration duration)  {
-		extractProbability(events, duration);
-		return extractor.getInvestigationProbabilityModifier();
-	}
+	protected abstract double getInvestigationProbability(Deque<Event> events, double currentProbability);
 
-	private double getEditingModifier(Deque<Event> events, Duration duration)  {
-		extractProbability(events, duration);
-		return extractor.getEditingProbabilityModifier();
-	}
+	protected abstract double getEditingProbability(Deque<Event> events, double currentProbability);
 
-	private double getVerificationModifier(Deque<Event> events, Duration duration)  {
-		extractProbability(events, duration);
-		return extractor.getVerificationProbabilityModifier();
-	}
+	protected abstract double getVerificationProbability(Deque<Event> events, double currentProbability);
 }
diff --git a/common/src/main/java/de/uni_bremen/agst/mimesis/persistence/events/visitor/ExtractPhaseShareBasedProbabilityVisitor.java b/common/src/main/java/de/uni_bremen/agst/mimesis/persistence/events/visitor/ExtractPhaseShareBasedProbabilityVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..29cb6344e8b5904ccf5cd856e596e0ea2a56de30
--- /dev/null
+++ b/common/src/main/java/de/uni_bremen/agst/mimesis/persistence/events/visitor/ExtractPhaseShareBasedProbabilityVisitor.java
@@ -0,0 +1,268 @@
+package de.uni_bremen.agst.mimesis.persistence.events.visitor;
+
+import de.uni_bremen.agst.mimesis.persistence.events.*;
+import lombok.Getter;
+
+/**
+ * Extracts the probability that a given event type
+ */
+public class ExtractPhaseShareBasedProbabilityVisitor implements EventVisitor {
+
+	/**
+	 * Probability modifier that the developer is currently in the investigation phase.
+	 * Will be multiplied with the initial probability of this phase.
+	 */
+	@Getter
+	private double investigationProbability;
+
+	/**
+	 * Probability modifier that the developer is currently in the editing phase.
+	 * Will be multiplied with the initial probability of this phase.
+	 */
+	@Getter
+	private double editingProbability;
+
+	/**
+	 * Probability modifier that the developer is currently in the verification phase.
+	 * Will be multiplied with the initial probability of this phase.
+	 */
+	@Getter
+	private double verificationProbability;
+
+	@Getter
+	private double investigationFrequencyScore;
+
+	@Getter
+	private double editingFrequencyScore;
+
+	@Getter
+	private double verificationFrequencyScore;
+
+	/**
+	 * Initializes a new visitor with the collection of recent events.
+	 */
+	public ExtractPhaseShareBasedProbabilityVisitor() {
+		reset();
+	}
+
+	/**
+	 * Resets all values to the default.
+	 */
+	private void reset() {
+		investigationProbability = 0;
+		editingProbability = 0;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0;
+	}
+
+	@Override
+	public void visit(CodeChangeEvent event) {
+		investigationProbability = 0.013146697456303;
+		editingProbability = 0.806578110483533;
+		verificationProbability = 0.180275192060164;
+		investigationFrequencyScore = 0.00333936283624377;
+		editingFrequencyScore = 0.216670469994924;
+		verificationFrequencyScore = 0.0625271557291902;
+
+	}
+
+	@Override
+	public void visit(CodeCompletionEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0.821386262495585;
+		verificationProbability = 0.178613737504415;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0.00245227679335341;
+		verificationFrequencyScore = 0.00144410860523393;
+
+	}
+
+	@Override
+	public void visit(DebugEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0.095633538708813;
+		verificationProbability = 0.904366461291187;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0.0000148104265402844;
+		verificationFrequencyScore = 0.000140056022408964;
+
+	}
+
+	@Override
+	public void visit(EditorMouseEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0;
+
+	}
+
+	@Override
+	public void visit(EditorTextCursorEvent event) {
+		investigationProbability = 0.104262985820664;
+		editingProbability = 0.678619341254429;
+		verificationProbability = 0.217117672924907;
+		investigationFrequencyScore = 0.0684842632728819;
+		editingFrequencyScore = 0.398904207483886;
+		verificationFrequencyScore = 0.155413564867674;
+
+	}
+
+	@Override
+	public void visit(LaunchEvent event) {
+		investigationProbability = 0.164140697056276;
+		editingProbability = 0.11448978059679;
+		verificationProbability = 0.721369522346934;
+		investigationFrequencyScore = 0.00477442401012157;
+		editingFrequencyScore = 0.00276436018434436;
+		verificationFrequencyScore = 0.0242490729727571;
+	}
+
+	@Override
+	public void visit(PerspectiveEvent event) {
+		investigationProbability = 0.79882399829807;
+		editingProbability = 0.062640225722219;
+		verificationProbability = 0.138535775979711;
+		investigationFrequencyScore = 0.0722785293992259;
+		editingFrequencyScore = 0.00559398916090297;
+		verificationFrequencyScore = 0.015366732439432;
+	}
+
+	@Override
+	public void visit(ProjectEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0;
+	}
+
+	@Override
+	public void visit(RecordingEvent event) {
+		investigationProbability = 0.316245369656478;
+		editingProbability = 0;
+		verificationProbability = 0.683754630343522;
+		investigationFrequencyScore = 0.00277477291036844;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0.0111000580226078;
+
+	}
+
+	@Override
+	public void visit(ResourceEvent event) {
+		investigationProbability = 0.163247626316087;
+		editingProbability = 0.243999680623975;
+		verificationProbability = 0.592752693059938;
+		investigationFrequencyScore = 0.00696094437567606;
+		editingFrequencyScore = 0.00810130009812491;
+		verificationFrequencyScore = 0.0245494020878547;
+	}
+
+	@Override
+	public void visit(SaveEvent event) {
+		investigationProbability = 0.010025986114882;
+		editingProbability = 0.248820768119129;
+		verificationProbability = 0.741153245765989;
+		investigationFrequencyScore = 0.000189753320683112;
+		editingFrequencyScore = 0.00310065176842037;
+		verificationFrequencyScore = 0.0116899481120217;
+	}
+
+	@Override
+	public void visit(ScrollEvent event) {
+		investigationProbability = 0.452728912256228;
+		editingProbability = 0.193274043616377;
+		verificationProbability = 0.353997044127395;
+		investigationFrequencyScore = 0.524984950190141;
+		editingFrequencyScore = 0.215334164537235;
+		verificationFrequencyScore = 0.396731057255983;
+	}
+
+	@Override
+	public void visit(SearchEvent event) {
+		investigationProbability = 0.173177102929252;
+		editingProbability = 0.349260287502385;
+		verificationProbability = 0.477562609568364;
+		investigationFrequencyScore = 0.00764163372859025;
+		editingFrequencyScore = 0.0011605765606206;
+		verificationFrequencyScore = 0.0216182355351368;
+	}
+
+	@Override
+	public void visit(TextCommentEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0;
+
+	}
+
+	@Override
+	public void visit(TextSelectionEvent event) {
+		investigationProbability = 0.287503224076869;
+		editingProbability = 0.435153397743742;
+		verificationProbability = 0.277343378179389;
+		investigationFrequencyScore = 0.0671112695567684;
+		editingFrequencyScore = 0.093580599641985;
+		verificationFrequencyScore = 0.0681679534377027;
+
+	}
+
+	@Override
+	public void visit(TreeViewerEvent event) {
+		investigationProbability = 0.985883620689655;
+		editingProbability = 0.014116379310345;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0.00465560075110805;
+		editingFrequencyScore = 0.0000289855072463768;
+		verificationFrequencyScore = 0;
+	}
+
+	@Override
+	public void visit(TreeSelectionEvent event) {
+		investigationProbability = 0.935798330544671;
+		editingProbability = 0.03753421103529;
+		verificationProbability = 0.026667458420039;
+		investigationFrequencyScore = 0.0498458800200679;
+		editingFrequencyScore = 0.00344333304588117;
+		verificationFrequencyScore = 0.00588235294117647;
+
+	}
+
+	@Override
+	public void visit(ViewEvent event) {
+		investigationProbability = 0.654731978633943;
+		editingProbability = 0.126850618560833;
+		verificationProbability = 0.218417402805224;
+		investigationFrequencyScore = 0.170837008576606;
+		editingFrequencyScore = 0.0373430419583147;
+		verificationFrequencyScore = 0.093718193787574;
+	}
+
+	@Override
+	public void visit(VoiceCommentEvent event) {
+		investigationProbability = 0;
+		editingProbability = 0;
+		verificationProbability = 0;
+		investigationFrequencyScore = 0;
+		editingFrequencyScore = 0;
+		verificationFrequencyScore = 0;
+	}
+
+	@Override
+	public void visit(WindowEvent event) {
+		investigationProbability = 0.140393295469157;
+		editingProbability = 0.113100049642076;
+		verificationProbability = 0.746506654888767;
+		investigationFrequencyScore = 0.0161216070515173;
+		editingFrequencyScore = 0.0115072328382208;
+		verificationFrequencyScore = 0.107402108183246;
+	}
+}
diff --git a/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/Settings.java b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/Settings.java
index c2bfa41aaffab41ba24718e641b9d7ee76c76525..57c0c594ff7e2627846ae563a243ec0688c07159 100644
--- a/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/Settings.java
+++ b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/Settings.java
@@ -4,6 +4,8 @@ import de.uni_bremen.agst.mimesis.visualization.model.settings.LineMappingMode;
 import de.uni_bremen.agst.mimesis.visualization.model.settings.SelectionExport;
 import de.uni_bremen.agst.mimesis.visualization.model.settings.TooltipDetail;
 import de.uni_bremen.agst.mimesis.visualization.model.settings.TooltipMode;
+import de.uni_bremen.agst.mimesis.visualization.model.settings.Filter;
+import org.primefaces.application.resource.DynamicResourcesPhaseListener;
 
 import javax.enterprise.context.SessionScoped;
 import java.io.Serializable;
@@ -61,6 +63,9 @@ public class Settings implements Serializable {
         add("Selection Export", "Whether to export a file after selecting an event range.",
                 SelectionExport.NO_DOWNLOAD, SelectionExport.class, SelectionExport::valueOf, null);
 
+        add("Phase filter", "Choose the filter used to determine the phase the developer is in.",
+                Filter.DECAY_PHASE_SHARE_BASED, Filter.class, Filter::valueOf, null);
+
     }
 
     /**
diff --git a/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/settings/Filter.java b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/settings/Filter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e0a769700496233ec54761ecdadabc53734a7ae
--- /dev/null
+++ b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/model/settings/Filter.java
@@ -0,0 +1,31 @@
+package de.uni_bremen.agst.mimesis.visualization.model.settings;
+
+import de.uni_bremen.agst.mimesis.filter.DecayPhaseShareBasedFilter;
+import de.uni_bremen.agst.mimesis.filter.FrequencyAccountedPhaseShareBasedFilter;
+import de.uni_bremen.agst.mimesis.filter.SimplePhaseShareBasedFilter;
+import de.uni_bremen.agst.mimesis.filter.ThreePhaseFiltering;
+
+public enum Filter {
+    DECAY_PHASE_SHARE_BASED("Use a filter that scores the phases using a multiplication based decay for the current value each time a new Event is added.", new DecayPhaseShareBasedFilter()),
+
+    FREQUENCY_ACCOUNTED_PHASE_SHARE_BASED("Uses a filter that attempts to compensate for the fact that some events are more frequent when determining phase scores.", new FrequencyAccountedPhaseShareBasedFilter()),
+
+    SIMPLE_PHASE_SHARE_BASED("Uses a filter that simply scores up the probability that each event type is in a certain phase for the last 100 Events for each events to determine the current phase.", new SimplePhaseShareBasedFilter());
+
+    private String description;
+    private ThreePhaseFiltering instance;
+
+    Filter(String description, ThreePhaseFiltering instance) {
+        this.description = description;
+        this.instance = instance;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s: %s", super.toString(), description);
+    }
+
+    public ThreePhaseFiltering getFilterInstance() {
+        return instance;
+    }
+}
diff --git a/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/service/GraphGeneratorService.java b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/service/GraphGeneratorService.java
index 45b0d63e96237764d6325121fdbe1bacf4034b81..c088e6688052058fa8f672910c3cb9acddbaffa9 100644
--- a/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/service/GraphGeneratorService.java
+++ b/visual/src/main/java/de/uni_bremen/agst/mimesis/visualization/service/GraphGeneratorService.java
@@ -1,5 +1,8 @@
 package de.uni_bremen.agst.mimesis.visualization.service;
-
+;
+import de.uni_bremen.agst.mimesis.filter.DecayPhaseShareBasedFilter;
+import de.uni_bremen.agst.mimesis.filter.FrequencyAccountedPhaseShareBasedFilter;
+import de.uni_bremen.agst.mimesis.filter.SimplePhaseShareBasedFilter;
 import de.uni_bremen.agst.mimesis.filter.ThreePhaseFiltering;
 import de.uni_bremen.agst.mimesis.persistence.EventContext;
 import de.uni_bremen.agst.mimesis.persistence.LineMapping;
@@ -9,6 +12,7 @@ import de.uni_bremen.agst.mimesis.persistence.locations.FileSpecificLocation;
 import de.uni_bremen.agst.mimesis.persistence.locations.LineSpecificLocation;
 import de.uni_bremen.agst.mimesis.visualization.model.EventListEntry;
 import de.uni_bremen.agst.mimesis.visualization.model.Settings;
+import de.uni_bremen.agst.mimesis.visualization.model.settings.Filter;
 import de.uni_bremen.agst.mimesis.visualization.model.settings.LineMappingMode;
 import de.uni_bremen.agst.mimesis.visualization.model.settings.TooltipDetail;
 import lombok.Getter;
@@ -36,7 +40,8 @@ public class GraphGeneratorService {
 		boolean includeInconsistentEvents = false;
 		return generateGraphForEventList(events, includeInconsistentEvents, fileName,
 				settings.<TooltipDetail>getSetting("Tooltip Detail").getValue(),
-				settings.<LineMappingMode>getSetting("Line Mapping Mode").getValue());
+				settings.<LineMappingMode>getSetting("Line Mapping Mode").getValue(),
+				((Filter) settings.getSetting("Phase filter").getValue()).getFilterInstance());
 	}
 
 	private static void applyFixes(Recording rec) {
@@ -203,7 +208,7 @@ public class GraphGeneratorService {
 	 * @return RecordingGraph for the given events.
 	 */
 	private static RecordingGraph generateGraphForEventList(List<Event> events, boolean includeInconsistentEvents,
-	                                                        String fileName, TooltipDetail detail, LineMappingMode mapMode) {
+	                                                        String fileName, TooltipDetail detail, LineMappingMode mapMode, ThreePhaseFiltering filtering) {
 		// This contains the maximum length each file requires to display all events within it
 		HashMap<String, Integer> fileMaxLengths = getMaxFileLengths(events, includeInconsistentEvents, mapMode);
 
@@ -224,16 +229,29 @@ public class GraphGeneratorService {
 
 		// Time at which the recording ended
 		long recordingEnd = events.get(events.size() - 1).getTimestamp();
-
-		List<EventDatapoint> eventData = getEventGraphDataset(events, offsets, includeInconsistentEvents, detail, mapMode);
+		// Generate event list entries and phase markings.
+		List<EventDatapoint> eventData = getEventGraphDataset(events, offsets, includeInconsistentEvents, detail, mapMode, filtering);
 		List<EventListEntry> entries = new ArrayList<>();
+		ArrayList<PhaseDatapoint> phases = new ArrayList<>();
+		String currentPhase = null;
+		long currentStartY = 0;
 		for (EventDatapoint datapoint : eventData) {
 			EventListEntry entry = EventListEntry.valueOf(datapoint.event);
 			entries.add(entry.withPhases(datapoint.getProbabilities()));
+			if(!datapoint.phaseName.equals(currentPhase)) {
+				if(currentPhase != null) {
+					phases.add(new PhaseDatapoint(currentPhase, currentStartY, datapoint.event.getTimestamp()));
+				}
+				currentStartY = datapoint.event.getTimestamp();
+				currentPhase = datapoint.phaseName;
+			}
+		}
+		if(currentPhase != null && eventData.size() > 0) {
+			phases.add(new PhaseDatapoint(currentPhase, currentStartY, eventData.get(eventData.size()-1).event.getTimestamp()));
 		}
 
 		// fileOffset is the maximum line number used in the graph
-		return new RecordingGraph(eventData, fileOffset, recordingEnd, offsets, fileName, entries);
+		return new RecordingGraph(eventData, fileOffset, recordingEnd, offsets, fileName, entries, phases);
 	}
 
 	/**
@@ -341,8 +359,8 @@ public class GraphGeneratorService {
 	 * @return An ArrayList of generated EventDatapoints.
 	 */
 	private static ArrayList<EventDatapoint> getEventGraphDataset(List<Event> events, HashMap<String, Integer> offsets,
-	                                                              boolean includeInconsistentEvents, TooltipDetail detail,
-	                                                              LineMappingMode mapMode) {
+																  boolean includeInconsistentEvents, TooltipDetail detail,
+																  LineMappingMode mapMode, ThreePhaseFiltering filtering) {
 		ArrayList<EventDatapoint> dataPoints = new ArrayList<>();
 		int lineNumber = 0;
 		int nonInferredLineNumber = 0;
@@ -350,7 +368,6 @@ public class GraphGeneratorService {
 		int overrideVisibleRangeBegin = 0;
 		int overrideVisibleRangeEnd = 0;
 		String filePath = "";
-		ThreePhaseFiltering filtering = new ThreePhaseFiltering();
 		for (Event event : events) {
 			//TODO: Instead of filtering the same thing every time, event data sets should be preprocessed once.
 			// Should probably be its own issue.
@@ -394,7 +411,7 @@ public class GraphGeneratorService {
 					}
 				}
 				filtering.handleEvent(event);
-				EventDatapoint eventFacade = new EventDatapoint(event, fileOffset, detail, filtering.getAllProbabilities());
+				EventDatapoint eventFacade = new EventDatapoint(event, fileOffset, detail, filtering.getAllProbabilities(), filtering.getMostLikelyPhaseName());
 				eventFacade.overrideEventLine(lineNumber);
 				eventFacade.overrideVisualRangeBegin(overrideVisibleRangeBegin);
 				eventFacade.overrideVisualRangeEnd(overrideVisibleRangeEnd);
@@ -447,6 +464,9 @@ public class GraphGeneratorService {
 		@Getter
 		private final LinkedHashMap<String, Integer> offsets;
 
+		@Getter
+		private final List<PhaseDatapoint> phases;
+
 		@Getter
 		private final int maxLine;
 
@@ -458,8 +478,9 @@ public class GraphGeneratorService {
 
 		//TODO: Use builder pattern
 		private RecordingGraph(List<EventDatapoint> eventDatapoints, int maxLine, long recordingEnd, Map<String, Integer> offsets,
-		                       String recordingName, List<EventListEntry> eventEntries) {
+		                       String recordingName, List<EventListEntry> eventEntries, List<PhaseDatapoint> phases) {
 			this.maxLine = maxLine;
+			this.phases = phases;
 			this.recordingEnd = recordingEnd;
 			this.recordingName = recordingName;
 			this.eventEntries = eventEntries;
@@ -484,6 +505,23 @@ public class GraphGeneratorService {
 		}
 	}
 
+	public static class PhaseDatapoint {
+		@Getter
+		private final String phaseName;
+
+		@Getter
+		private final long begin;
+
+		@Getter
+		private final long end;
+
+		public PhaseDatapoint(String phaseName, long begin, long end) {
+			this.phaseName = phaseName;
+			this.begin = begin;
+			this.end = end;
+		}
+	}
+
 
 	/**
 	 * Facade class for events that can be provided with override values for attributes in case they are not set by the
@@ -505,11 +543,15 @@ public class GraphGeneratorService {
 
 		private Map<String, Double> probabilities;
 
-		EventDatapoint(Event event, int offset, TooltipDetail detail, Map<String, Double> probabilities) {
+		@Getter
+		private String phaseName;
+
+		EventDatapoint(Event event, int offset, TooltipDetail detail, Map<String, Double> probabilities, String phaseName) {
 			this.event = event;
 			this.offset = offset;
 			this.detail = detail;
 			this.probabilities = probabilities;
+			this.phaseName = phaseName;
 		}
 
 		/**
diff --git a/visual/src/main/webapp/resources/eventGraph.js b/visual/src/main/webapp/resources/eventGraph.js
index 05b320dbeaf95e3e0bb871db97994cf9944c5811..2b0f5a35860b2b7536322f12d0208edecb6639e3 100644
--- a/visual/src/main/webapp/resources/eventGraph.js
+++ b/visual/src/main/webapp/resources/eventGraph.js
@@ -325,6 +325,9 @@ function drawGraph() {
         // Events are separated into "tracks", which currently are just files
         chart.options.annotation.annotations = generateAnnotations(data);
 
+        chart.options.annotation.fileTrackCount = data.offsets.length;
+        chart.options.annotation.phaseTrackCount = data.phases.length;
+
         // Now we can generate the legend
         loadLegend(chart.generateLegend());
 
@@ -355,6 +358,9 @@ function legendGenerator(theChart, typeSet = theChart.data.datasets[1].types) {
         text.push('<li><span id="legend-track-item" style="background-color: #cc65fe; color: black; ' +
             'user-select: none" onclick="toggleTrackBackgroundVisibility()">Tracks</span></li>');
 
+        text.push('<li><span id="legend-phase-item" style="background-color: #888888; text-decoration: line-through; color: black; ' +
+            'user-select: none" onclick="togglePhaseBackgroundVisibility()">Phases</span></li>');
+
         text.push('<br />');
     }
 
@@ -426,6 +432,7 @@ function generateAnnotations(data) {
 
          // First we add the box which contains the track
          annots.push({
+             kind: 'track',
              type: 'box',
              xScaleID: 'LineXAxis',
              yScaleID: 'TimeYAxis',
@@ -437,6 +444,7 @@ function generateAnnotations(data) {
          });
          // Then we add a vertical line, but it's just to add a label (no other way right now)
          annots.push({
+             kind: 'track',
              type: 'line',
              mode: 'vertical',
              scaleID: 'LineXAxis',
@@ -459,6 +467,28 @@ function generateAnnotations(data) {
          }
          i += 2;
      }
+    for (let phaseIndex in data.phases) {
+        const phase = data.phases[phaseIndex];
+        var phaseColor = "#FFFFFF00";
+        if(phase.phaseName === "Investigation") {
+            phaseColor = "#FF000000";
+        } else if (phase.phaseName === "Editing") {
+            phaseColor = "#00FF0000";
+        } else if (phase.phaseName === "Verification") {
+            phaseColor = "#0000FF00";
+        }
+        annots.push({
+            kind: 'phase',
+            type: '_box',
+            xScaleID: 'LineXAxis',
+            yScaleID: 'TimeYAxis',
+            yMin: phase.begin,
+            yMax: phase.end,
+            backgroundColor: phaseColor,
+            borderWidth: 0,
+            borderColor: phaseColor
+        });
+    }
     return annots;
 }
 
diff --git a/visual/src/main/webapp/resources/eventLegend.js b/visual/src/main/webapp/resources/eventLegend.js
index 5ab55a42f17165806ddbe47d8d5cc712992f50ea..508d09f5d9b5a344566cfcb30bdcdb4e1c66e521 100644
--- a/visual/src/main/webapp/resources/eventLegend.js
+++ b/visual/src/main/webapp/resources/eventLegend.js
@@ -62,8 +62,9 @@ function toggleTrackBackgroundVisibility() {
     // by setting its type to an empty string. However, we still need to recognize later on which element was a box
     // and which was a line, so we use invalid but unique names.
 
-    // If there are no annotations, we return, since we can't change this.
+    // If there are no track annotations, we return, since we can't change this.
     if (annotations.length === 0) return;
+    if (annotations[0].kind !== "track") return;
 
     // First, we check which state S we're in. This implies we want to reach state (S+1)%3.
     let thisState;
@@ -74,24 +75,25 @@ function toggleTrackBackgroundVisibility() {
     // Then we apply the trick described above
     for (let i = 0; i < annotations.length; i++) {
         let thisAnnotation = annotations[i];
-
-        if (thisState === 0) {
-            if (thisAnnotation.type === "box") {
-                // We make the box background invisible using RGBA
-                thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "00";
-            }
-        } else if (thisState === 1) {
-            if (thisAnnotation.type === "line") {
-                thisAnnotation.type = "_line";
-            } else if (thisAnnotation.type === "box") {
-                thisAnnotation.type = "_box";
-            }
-        } else {
-            if (thisAnnotation.type === "_box") {
-                thisAnnotation.type = "box";
-                thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "22";
-            } else if (thisAnnotation.type === "_line") {
-                thisAnnotation.type = "line";
+        if(thisAnnotation.kind === "track") {
+            if (thisState === 0) {
+                if (thisAnnotation.type === "box") {
+                    // We make the box background invisible using RGBA
+                    thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "00";
+                }
+            } else if (thisState === 1) {
+                if (thisAnnotation.type === "line") {
+                    thisAnnotation.type = "_line";
+                } else if (thisAnnotation.type === "box") {
+                    thisAnnotation.type = "_box";
+                }
+            } else {
+                if (thisAnnotation.type === "_box") {
+                    thisAnnotation.type = "box";
+                    thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "22";
+                } else if (thisAnnotation.type === "_line") {
+                    thisAnnotation.type = "line";
+                }
             }
         }
     }
@@ -108,6 +110,50 @@ function toggleTrackBackgroundVisibility() {
     window.chart.update();
 }
 
+// For phases we only have two possible states.
+// 1: fully visible, 2: fully invisible, which is again recognizable by:
+// 1: colored cyan, 2: colored grey and text as strikethrough
+function togglePhaseBackgroundVisibility() {
+    let annotations = window.chart.options.annotation.annotations;
+    // A bit hacky, but basically the only way to hide an annotation is to make it invalid, for example
+    // by setting its type to an empty string. However, we still need to recognize later on which element was a box
+    // and which was a line, so we use invalid but unique names.
+
+    // If there are no phase annotations, we return, since we can't change this.
+    if (annotations.length === 0) return;
+    if (annotations[annotations.length-1].kind !== "phase") return;
+
+    // First, we check which state S we're in. This implies we want to reach state (S+1)%3.
+    let thisState;
+    if (annotations[annotations.length-1].backgroundColor[8] !== '0') thisState = 0; // If box is not transparent
+    else thisState = 1;
+
+    // Then we apply the trick described above
+    for (let i = 0; i < annotations.length; i++) {
+        let thisAnnotation = annotations[i];
+        if(thisAnnotation.kind === "phase") {
+            if (thisState === 0) {
+                // We make the box background invisible using RGBA
+                thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "00";
+                thisAnnotation.type = "_box";
+            } else {
+                thisAnnotation.type = "box";
+                thisAnnotation.backgroundColor = thisAnnotation.backgroundColor.substring(0, 7) + "44";
+            }
+        }
+    }
+
+    if (thisState === 0) {
+        document.getElementById("legend-phase-item").style.backgroundColor = "#888888";
+        document.getElementById("legend-phase-item").style.textDecoration = "line-through";
+    } else {
+        document.getElementById("legend-phase-item").style.textDecoration = "";
+        document.getElementById("legend-phase-item").style.backgroundColor = "#65ccfe";
+    }
+
+    window.chart.update();
+}
+
 /**
  * Toggles visibility of all data points that fall within a certain x-coordinate range.
  * Right now, tracks can only be disabled. This is done destructively and can't be reversed without reloading.