diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 574de4568e21706c1ac832f7025a60df46d8ae69..7ddfc9ed476345b09db3e8fdf464a62bb2ccfa9b 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -8,6 +8,5 @@
   </component>
   <component name="VcsDirectoryMappings">
     <mapping directory="" vcs="Git" />
-    <mapping directory="$PROJECT_DIR$/idatt2003" vcs="Git" />
   </component>
 </project>
\ No newline at end of file
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/Main.java b/src/main/java/edu/ntnu/stud/chaosgame/Main.java
index 58d22a067efb0c50b4f9d1baaae4fd42e989d4b0..9b27b8b6dcdb93f6d9ddc72db03738a675ed10de 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/Main.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/Main.java
@@ -1,6 +1,6 @@
 package edu.ntnu.stud.chaosgame;
 
-import edu.ntnu.stud.chaosgame.view.ChaosGameGuiView;
+import edu.ntnu.stud.chaosgame.view.ChaosGameGui;
 import javafx.application.Application;
 import javafx.stage.Stage;
 
@@ -13,7 +13,7 @@ public class Main extends Application {
 
   @Override
   public void start(Stage primaryStage) throws IOException {
-    ChaosGameGuiView view = new ChaosGameGuiView(primaryStage);
+    ChaosGameGui view = new ChaosGameGui(primaryStage);
 
   }
 
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosCanvas.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosCanvas.java
index d0c91770c4d691c6ad94538084fb31363df2a035..f828143ea069922cd25511fe2b9881d31f52965a 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosCanvas.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosCanvas.java
@@ -15,6 +15,12 @@ public class ChaosCanvas {
    */
   private final int[][] canvas;
 
+  /**
+   * This array keeps track of how many times a pixel has been visited,
+   * to be used to determine the color of the pixel when displayed.
+   */
+  private final int[][] canvasIntensityArray;
+
   /**
    * Width of the canvas
    */
@@ -36,6 +42,9 @@ public class ChaosCanvas {
    */
   private Vector2D maxCoords;
 
+  private int centreX;
+  private int centreY;
+
   /**
    * Affine transformation for converting coordinates to canvas indices.
    */
@@ -54,8 +63,11 @@ public class ChaosCanvas {
     this.height = height;
     this.minCoords = minCoords;
     this.maxCoords = maxCoords;
+    System.out.println("Min: " + minCoords.getX0() + " " + maxCoords.getX0() + ", Max: " + maxCoords.getX0()+ " " + maxCoords.getX1());
 
+    // Instantiate the canvas and its intensity array.
     this.canvas = new int[width][height];
+    this.canvasIntensityArray = new int[width][height];
 
     // Convert the coordinates to indices in the canvas
     this.transformCoordsToIndices = new AffineTransform2D(
@@ -65,34 +77,34 @@ public class ChaosCanvas {
             (width-1)* minCoords.getX0() / (minCoords.getX0() - maxCoords.getX0())));
   }
 
-  /**
-   * Get a pixel located at a point.
-   *
-   * @param point point at which the pixel is located
-   * @return the pixel
-   */
-  public int getPixel(Vector2D point) {
-    return canvas[ (int) transformCoordsToIndices.transform(point).getX0()]
-        [(int) transformCoordsToIndices.transform(point).getX1()];
-  }
-
   /**
    * Place a pixel on the canvas.
    *
    * @param point the point where the pixel is to be placed.
    */
-  public void putPixel(Vector2D point) {
-
+  public void putPixel(Vector2D point, boolean countIntensity) {
+    if (point.getX0() > maxCoords.getX0() || point.getX0() < minCoords.getX0() ||
+        point.getX1() > maxCoords.getX1() || point.getX1() < minCoords.getX1()) {
+      //PopupManager.displayError("Point out of bounds",
+        //  "The point is out of bounds of the canvas.");
+      return;
+    }
     int xIndex = (int) transformCoordsToIndices.transform(point).getX0();
     int yIndex = (int) transformCoordsToIndices.transform(point).getX1();
 
     if (xIndex >= 0 && xIndex < width && yIndex >= 0 && yIndex < height) {
         canvas[xIndex][yIndex] = 1;
+
+        // If the countIntensity variable is true, increment the intensity of the pixel.
+        if (countIntensity) {
+            canvasIntensityArray[xIndex][yIndex]++;
+        }
     } else {
         System.out.println("Index out of bounds: " + xIndex + ", " + yIndex);
     }
 }
 
+
   /**
    * Get the width and height of the canvas in the form of an array where the first
    * index stores the width and the second stores the height.
@@ -117,6 +129,33 @@ public class ChaosCanvas {
    */
   public Vector2D getMaxCoords() { return this.maxCoords; }
 
+  /**
+   * Get a pixel located at a point.
+   *
+   * @param point point at which the pixel is located
+   * @return the pixel
+   */
+  public int getPixel(Vector2D point) {
+    return canvas[ (int) transformCoordsToIndices.transform(point).getX0()]
+        [(int) transformCoordsToIndices.transform(point).getX1()];
+  }
+
+  /**
+   * Get the intensity of a pixel located at a point.
+   *
+   * @param point the point to check for.
+   * @return the intensity at the point.
+   */
+  public int getIntensityPixel(Vector2D point) {
+    return canvasIntensityArray[ (int) transformCoordsToIndices.transform(point).getX0()]
+        [(int) transformCoordsToIndices.transform(point).getX1()];
+  }
+
+  /**
+   * @return the canvas intensity array.
+   */
+  public int[][] getCanvasIntensityArray() { return this.canvasIntensityArray; }
+
   /**
    * Get the number of points in the canvas with a value of 1.
    *
@@ -178,6 +217,43 @@ public class ChaosCanvas {
     }
   }
 
+  /**
+   * Updates the minimum and maximum coordinates of the canvas based on the
+   * given center coordinates and zoom level.
+   *
+   * @param centreX the x-coordinate of the center of the subsection
+   * @param centreY the y-coordinate of the center of the subsection
+   * @param zoomLevel the zoom level
+   */
+  public void updateCoords(double centreX, double centreY, int  zoomLevel) {
+    // Compute the size of the subsection based on the zoom level
+    double size = Math.pow(4, -zoomLevel); // Adjust this formula as needed
+
+    // Update the minimum and maximum coordinates
+    minCoords = new Vector2D(centreX - size / 2, centreY - size / 2);
+    maxCoords = new Vector2D(centreX + size / 2, centreY + size / 2);
+    // TODO: remove tests
+    System.out.println("Min: " + minCoords.getX0() + " " + maxCoords.getX0() + ", Max: " + maxCoords.getX0()+ " " + maxCoords.getX1());
+
+    // Update the affine transformation for converting coordinates to canvas indices
+    transformCoordsToIndices = new AffineTransform2D(
+        new Matrix2x2(0, (height-1) / (minCoords.getX1() - maxCoords.getX1()),
+            (width-1) / (maxCoords.getX0() - minCoords.getX0()), 0),
+        new Vector2D(((height-1)* maxCoords.getX1()) / (maxCoords.getX1() - minCoords.getX1()),
+            (width-1)* minCoords.getX0() / (minCoords.getX0() - maxCoords.getX0())));
+  }
+
+  /**
+   * Checks whether a given point is within the current subsection of the coordinate space.
+   *
+   * @param point the point to check
+   * @return true if the point is within the subsection, false otherwise
+   */
+  public boolean isPointInCanvasRange(Vector2D point) {
+    return point.getX0() >= minCoords.getX0() && point.getX0() <= maxCoords.getX0()
+        && point.getX1() >= minCoords.getX1() && point.getX1() <= maxCoords.getX1();
+  }
+
   public int getWidth() {
     return width;
   }
@@ -185,4 +261,13 @@ public class ChaosCanvas {
   public int getHeight() {
     return height;
   }
+
+  /**
+   * Get the centre coordinates.
+   *
+   * @return the centre.
+   */
+  public int[] getCentre() {
+    return new int[]{this.centreX, this.centreY};
+  }
 }
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java
index cf139bf106e515321e444521a1c77235c7f57f8b..0521e77de43a953a63ee491ab5c80101574d8647 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGame.java
@@ -42,6 +42,12 @@ public class ChaosGame {
    */
   private int numOfTransforms;
 
+  /**
+   * Boolean informing the game whether to use colors for more frequent
+   * pixels or not.
+   */
+  private boolean useColor = false;
+
   /**
    * Basic parameterised constructor.
    *
@@ -87,6 +93,22 @@ public class ChaosGame {
    */
   public Vector2D getCurrentPoint() { return this.currentPoint; }
 
+  /**
+   * Get the random number generator for this chaos game.
+   *
+   * @return the RNG.
+   */
+  public Random getRandom() { return this.random; }
+
+  /**
+   * Get the description for this choas game.
+   *
+   * @return the description.
+   */
+  public ChaosGameDescription getDescription() {
+    return this.description;
+  }
+
   /**
    * Get the chaos game description.
    *
@@ -96,6 +118,25 @@ public class ChaosGame {
     this.description = description;
   }
 
+  /**
+   * Set the current point from which the chaos game will proceed to iterate over
+   * the set of affine transforms in the chaos game description.
+   *
+   * @param point the new point.
+   */
+  public void setCurrentPoint(Vector2D point) {
+    this.currentPoint = point;
+  }
+
+  /**
+   * Set the useColor boolean.
+   *
+   * @param useColor the boolean to set.
+   */
+  public void setUseColor(boolean useColor) {
+    this.useColor = useColor;
+  }
+
 
   /**
    * Notify the observers that a change has occurred in the ChaosGame.
@@ -128,34 +169,35 @@ public class ChaosGame {
     for (int i = 0; i < n; i++) {
 
       int j = this.random.nextInt( this.numOfTransforms);
-      switch (j) {
-        case 0:
-          results.set(0, results.get(0) + 1);
-          break;
-        case 1:
-          results.set(1, results.get(1) + 1);
-          break;
-        case 2:
-          results.set(2, results.get(2) + 1);
-          break;
-        case 3:
-          results.set(3, results.get(3) + 1);
-          break;
-      }
+
       //System.out.println(j);
       //System.out.println(this.description.getTransforms().size());
 
       //System.out.println("Before transform: " + currentPoint.getX0() + " " + currentPoint.getX1());
+      Vector2D newPoint = this.description.getTransforms().get(j).transform(currentPoint);
 
-
-      this.currentPoint = this.description.getTransforms().get(j).transform(currentPoint);
-      //System.out.println("After transform: " + currentPoint.getX0() + " " + currentPoint.getX1());
-      this.canvas.putPixel(currentPoint);
+      this.currentPoint = this.findValidPoint(newPoint);
+      this.canvas.putPixel(currentPoint, useColor);
     }
-    //this.canvas.printCanvas();
-    for (int i = 0; i < results.size(); i++) {
-      System.out.println("Transform " + i + " was chosen " + results.get(i) + " times.");
+
+  }
+
+  /**
+   * If the point is out of bounds, iterate until it is within bounds. Used
+   * if the useColor boolean is set to false.
+   *
+   * @param point the starting point.
+   * @return the resulting valid point within bounds.
+   */
+  public Vector2D findValidPoint(Vector2D point) {
+    if (!this.canvas.isPointInCanvasRange(point) || this.canvas.getPixel(point) == 1) {
+      while (!this.canvas.isPointInCanvasRange(point)) {
+        int j = this.random.nextInt(this.numOfTransforms);
+        System.out.println("Before transform: " + point.getX0() + " " + point.getX1()); // Test
+        point = this.description.getTransforms().get(j).transform(currentPoint);
+      }
     }
+    return point;
   }
 
 }
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameFileHandler.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameFileHandler.java
index 0f60eb77693e7a66a108f3c138d435b12cf7cdc6..fc93a525a4cd406a42d417b6c05d4aa94ed1868b 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameFileHandler.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameFileHandler.java
@@ -42,8 +42,6 @@ public class ChaosGameFileHandler {
 
       if (firstLine.equals("Affine2D")) {
 
-
-
         // Read the minimum vector
         if (scanner.hasNextDouble()) {
           double x0min = scanner.nextDouble();
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameTask.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..d745f3c814ac9b56968603870c988e78c09fe039
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/ChaosGameTask.java
@@ -0,0 +1,56 @@
+package edu.ntnu.stud.chaosgame.controller.game;
+import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
+import edu.ntnu.stud.chaosgame.model.data.Vector2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.*;
+
+// Step 1: Create a Callable tasks
+class ChaosGameTask implements Callable<Void> {
+  private final int start;
+  private final int end;
+  private final ChaosGame game;
+
+  public ChaosGameTask(ChaosGame game, int start, int end) {
+    this.game = game;
+    this.start = start;
+    this.end = end;
+  }
+
+  @Override
+  public Void call() {
+    for (int i = start; i < end; i++) {
+      int j = game.getRandom().nextInt(game.getDescription().getTransforms().size());
+      Vector2D newPoint = game.getDescription().getTransforms().get(j).transform(game.getCurrentPoint());
+      game.setCurrentPoint(game.findValidPoint(newPoint));
+      game.getCanvas().putPixel(game.getCurrentPoint(), false);
+    }
+    return null;
+  }
+
+  // Step 2, 3, 4: Divide the task into smaller tasks and execute them in parallel
+  public void runSteps(int n) {
+    int numProcessors = Runtime.getRuntime().availableProcessors();
+    ExecutorService executor = Executors.newFixedThreadPool(numProcessors);
+
+    int chunkSize = n / numProcessors;
+    List<Future<Void>> futures = new ArrayList<>();
+
+    for (int i = 0; i < numProcessors; i++) {
+      int start = i * chunkSize;
+      int end = (i == numProcessors - 1) ? n : start + chunkSize; // handle remainder
+      Callable<Void> task = new ChaosGameTask(this.game, start, end);
+      futures.add(executor.submit(task));
+    }
+
+    for (Future<Void> future : futures) {
+      try {
+        future.get(); // wait for task to complete
+      } catch (InterruptedException | ExecutionException e) {
+        e.printStackTrace();
+      }
+    }
+
+    executor.shutdown(); // always remember to shutdown the executor
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/Quadtree.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/Quadtree.java
new file mode 100644
index 0000000000000000000000000000000000000000..32915e43675e4d141cd806ceb5f0f8e4eb3e7a71
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/Quadtree.java
@@ -0,0 +1,34 @@
+package edu.ntnu.stud.chaosgame.controller.game;
+
+import javafx.geometry.Point2D;
+import javafx.scene.shape.Rectangle;
+
+public class Quadtree {
+  private QuadtreeNode root;
+
+  public Quadtree(Rectangle bounds) {
+    root = new QuadtreeNode(bounds, 0);
+  }
+
+  public QuadtreeNode findNodeContainingPoint(Point2D point) {
+    return findNodeContainingPoint(root, point);
+  }
+
+  private QuadtreeNode findNodeContainingPoint(QuadtreeNode node, Point2D point) {
+    if (node.isLeaf()) {
+      return node;
+    }
+
+    for (QuadtreeNode child : node.getChildren()) {
+      if (child.getBounds().contains(point)) {
+        return findNodeContainingPoint(child, point);
+      }
+    }
+
+    return null;
+  }
+
+
+
+  // other methods omitted for brevity
+}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/game/QuadtreeNode.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/QuadtreeNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea7b7940e476da2c71ee83c7182f1bcbba1d2afa
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/game/QuadtreeNode.java
@@ -0,0 +1,111 @@
+package edu.ntnu.stud.chaosgame.controller.game;
+
+import javafx.scene.shape.Rectangle;
+
+public class QuadtreeNode {
+  private QuadtreeNode parent;
+  private QuadtreeNode[] children;
+
+  /**
+   * Boolean representing whether the node is a leaf node or
+   * a child node.
+   */
+  private boolean isLeaf;
+
+  /**
+   * Integer describing which recursion level this node belongs to.
+   */
+  private int recursionLevel;
+  private Rectangle bounds;
+
+  private double minX;
+  private double minY;
+  private double maxX;
+  private double maxY;
+
+
+  public QuadtreeNode(Rectangle bounds, int recursionLevel) {
+    this.bounds = bounds;
+    this.minX = bounds.getX();
+    this.minY = bounds.getY() - bounds.getHeight();
+    this.maxX = bounds.getX() + bounds.getWidth();
+    this.maxY = bounds.getY();
+
+    this.recursionLevel = recursionLevel;
+    this.isLeaf = true;
+    this.children = new QuadtreeNode[4];
+  }
+
+  /**
+   * Split this parent node into four child nodes.
+   */
+  public void split() {
+    if (!isLeaf) {
+      return;
+    }
+
+    double halfWidth = bounds.getWidth() / 2;
+    double halfHeight = bounds.getHeight() / 2;
+
+    children[0] = new QuadtreeNode(new Rectangle(bounds.getX(), bounds.getY(), halfWidth, halfHeight), recursionLevel
+        + 1);
+    children[1] = new QuadtreeNode(new Rectangle(bounds.getX() + halfWidth, bounds.getY(), halfWidth, halfHeight), recursionLevel
+        + 1);
+    children[2] = new QuadtreeNode(new Rectangle(bounds.getX(), bounds.getY() + halfHeight, halfWidth, halfHeight), recursionLevel
+        + 1);
+    children[3] = new QuadtreeNode(new Rectangle(bounds.getX() + halfWidth, bounds.getY() + halfHeight, halfWidth, halfHeight), recursionLevel
+        + 1);
+
+    isLeaf = false;
+  }
+
+  /**
+   * Merge this node with its three counterparts to form a single child node.
+   */
+  public void merge() {
+    if (isLeaf) {
+      return;
+    }
+
+    for (int i = 0; i < 4; i++) {
+      children[i] = null;
+    }
+
+    isLeaf = true;
+  }
+
+  /**
+   * Check if this node contains or is adjacent to a specified point in the plane.
+   *
+   * @param centerX the centre of the point in the x-axis.
+   * @param centerY the centre of the point in the y-axis.
+   * @return a boolean representing the result o the check.
+   */
+  public boolean containsOrAdjacent(double centerX, double centerY) {
+    // Check if the point is within the node
+    if (centerX >= this.minX && centerX <= this.maxX && centerY >= this.minY && centerY <= this.maxY) {
+      return true;
+    }
+
+    // Check if the point is adjacent to the node
+    if (centerX >= this.minX - 1 && centerX <= this.maxX + 1 && centerY >= this.minY - 1 && centerY <= this.maxY + 1) {
+      return true;
+    }
+
+    return false;
+  }
+
+  public Rectangle getBounds() {
+    return this.bounds;
+  }
+
+  public QuadtreeNode[] getChildren() {
+    return this.children;
+  }
+
+  public Boolean isLeaf() {
+    return this.isLeaf;
+  }
+
+  // getters and setters omitted for brevity
+}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/Formatter.java b/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/Formatter.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa4045a091c44980b024615a170ed66b5292a535
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/controller/utility/Formatter.java
@@ -0,0 +1,53 @@
+package edu.ntnu.stud.chaosgame.controller.utility;
+
+import java.util.function.UnaryOperator;
+import javafx.scene.control.TextFormatter;
+import javafx.scene.control.TextFormatter.Change;
+
+/**
+ * This class sets formatting constraints for certain UI components.
+ */
+public class Formatter {
+
+
+  /**
+   * A formatter for text fields that only allows for floating point numbers.
+   */
+  public static UnaryOperator<Change> floatFormatter = change -> {
+    String newText = change.getControlNewText();
+    if (newText.matches("([0-9]*[.])?[0-9]*")) {
+      return change;
+    }
+    return null;
+  };
+
+  /**
+   * A formatter for text fields that only allows for integers.
+   */
+  public static UnaryOperator<TextFormatter.Change> integerFormatter = change -> {
+    String newText = change.getControlNewText();
+    if (newText.matches("[0-9]*")) {
+        return change;
+    }
+    return null;
+};
+
+
+  /**
+   * Get the float formatter as a TextFormatter.
+   *
+   * @return the float formatter.
+   */
+  public static TextFormatter<Change> getFloatFormatter() {
+    return new TextFormatter<>(floatFormatter);
+  }
+
+  /**
+   * Get the integer formatter as a TextFormatter.
+   *
+   * @return the integer formatter.
+   */
+  public static TextFormatter<Change> getIntFormatter() {
+    return new TextFormatter<>(integerFormatter);
+  }
+}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosCanvasToImageConverter.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosCanvasToImageConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..074dcc7ca1ff4b2ecf80d97cb5ca287988dbcf29
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosCanvasToImageConverter.java
@@ -0,0 +1,102 @@
+package edu.ntnu.stud.chaosgame.view;
+import javafx.scene.image.PixelWriter;
+import javafx.scene.image.WritableImage;
+import javafx.scene.paint.Color;
+import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
+
+/**
+ * This class converts the state of a ChaosCanvas to a WritableImage.
+ */
+public class ChaosCanvasToImageConverter {
+
+  /**
+   * The image to be created.
+   */
+  private WritableImage image;
+
+  /**
+   * Convert the canvas to a writable image.
+   *
+   * @param chaosCanvas the canvas to work upon.
+   */
+  public ChaosCanvasToImageConverter(ChaosCanvas chaosCanvas, boolean useIntensity) {
+    if (!useIntensity) {
+      this.convertWithoutIntensity(chaosCanvas);
+    } else {
+      this.convertWithIntensity(chaosCanvas);
+    }
+  }
+
+
+  /**
+   * Get the image.
+   *
+   * @return the image.
+   */
+  public WritableImage getImage() {
+    return image;
+  }
+
+  /**
+   * Convert the ChaosCanvas to an image using the intensity of the pixels
+   * to determine the color.
+   *
+   * @param canvas the ChaosCanvas to convert.
+   */
+  public void convertWithIntensity(ChaosCanvas canvas) {
+    int width = canvas.getWidth();
+    int height = canvas.getHeight();
+    image = new WritableImage(width, height);
+    PixelWriter pixelWriter = image.getPixelWriter();
+
+    int[][] canvasArray = canvas.getCanvasArray();
+    int[][] canvasIntensityArray = canvas.getCanvasIntensityArray();
+    int maxIntensity = 0;
+
+    // Find the maximum intensity.
+    for (int i = 0; i < width; i++) {
+      for (int j = 0; j < height; j++) {
+        if (canvasArray[i][j] == 1 && canvasIntensityArray[i][j] > maxIntensity) {
+          maxIntensity = canvasIntensityArray[i][j];
+        }
+      }
+    }
+
+    // Map the intensity to a hue value.
+    for (int i = 0; i < width; i++) {
+      for (int j = 0; j < height; j++) {
+        if (canvasArray[i][j] == 1) {
+          int intensity = canvasIntensityArray[i][j];
+          double hue = (double) intensity / maxIntensity * 360; // Map intensity to hue
+          pixelWriter.setColor(j, i, Color.hsb(hue, 1.0, 1.0)); // Full saturation and brightness
+        } else {
+          pixelWriter.setColor(j, i, Color.WHITE);
+        }
+      }
+    }
+  }
+
+  /**
+   * Convert the ChaosCanvas to an image without concern for the intensity of the pixels.
+   *
+   * @param chaosCanvas the ChaosCanvas to convert.
+   */
+  public void convertWithoutIntensity(ChaosCanvas chaosCanvas) {
+    int width = chaosCanvas.getWidth();
+    int height = chaosCanvas.getHeight();
+    image = new WritableImage(width, height);
+    PixelWriter pixelWriter = image.getPixelWriter();
+
+    int[][] canvasArray = chaosCanvas.getCanvasArray();
+    for (int i = 0; i < width; i++) {
+      for (int j = 0; j < height; j++) {
+        if (canvasArray[i][j] == 1) {
+          pixelWriter.setColor(j, i, Color.BLACK);
+        } else {
+          pixelWriter.setColor(j, i, Color.WHITE);
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java
new file mode 100644
index 0000000000000000000000000000000000000000..baea5bfd55c60d9ef4debd535693bfe61392abfe
--- /dev/null
+++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGui.java
@@ -0,0 +1,495 @@
+package edu.ntnu.stud.chaosgame.view;
+
+import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
+import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
+import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
+import edu.ntnu.stud.chaosgame.controller.utility.Formatter;
+import edu.ntnu.stud.chaosgame.model.data.Vector2D;
+import edu.ntnu.stud.chaosgame.model.generators.ChaosGameDescriptionFactory;
+
+import java.util.Objects;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.animation.TranslateTransition;
+import javafx.application.Platform;
+import javafx.geometry.Insets;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.control.*;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.image.WritableImage;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+import java.io.IOException;
+
+public class ChaosGameGui implements ChaosGameObserver {
+  private int currentLine = 0;
+
+
+  private Canvas canvas;
+
+  /**
+   * The ChaosCanvas for this GUI.
+   */
+  private ChaosCanvas chaosCanvas;
+
+  /**
+   * The ChaosGameDescription.
+   */
+  private ChaosGameDescription description;
+
+  /**
+   * The ChaosGameDescriptionFactory.
+   */
+  private ChaosGameDescriptionFactory factory;
+
+  /**
+   * The ImageView for the GUI..
+   */
+  private ChaosGameImageView imageView;
+
+  /**
+   * The Scene for the GUI..
+   */
+  private Scene scene;
+
+  /**
+   * The width and height of the GUI.
+   */
+  private int width;
+  private int height;
+
+  /**
+   * The ChaosGame for this GUI.
+   */
+  private ChaosGame game;
+
+  /**
+   * The Timeline for the GUI.
+   */
+  private Timeline timeline;
+
+  /**
+   * The BorderPane for the GUI.
+   */
+  private BorderPane borderPane;
+
+  /**
+   * The side menu for the GUI.
+   */
+  private VBox sideMenu;
+
+  /**
+   * The start, stop, new, clear, quit and show sidebar buttons for the GUI.
+   */
+  private Button startButton;
+  private Button stopButton;
+  private Button newButton;
+  private Button clearButton;
+  private Button quitButton;
+  private Button sideMenuButton;
+
+  /**
+   * The load fractal from file and write fractal to file buttons for the GUI.
+   */
+  private Button loadFractalFromFileButton;
+  private Button writeFractalToFileButton;
+
+  /**
+   * The radio buttons for the fractal type for the GUI.
+   */
+  private RadioButton sierpinskiRadioButton;
+  private RadioButton barnsleyRadioButton;
+  private RadioButton juliaRadioButton;
+  private RadioButton improvedBarnsleyButton;
+
+  private TextField stepCountTextField;
+
+  private CheckBox colorCheckBox;
+
+
+  public ChaosGameGui(Stage primaryStage) throws IOException {
+
+    this.initializeComponents();
+
+    primaryStage.setTitle("Fractal Chaos Game");
+    primaryStage.setScene(scene);
+    primaryStage.setOnShown(event -> this.imageView.requestFocus());
+    primaryStage.show();
+
+  }
+
+  /**
+   * Initialize the components of the GUI.
+   */
+  private void initializeComponents() {
+
+    // Timeline
+    this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame()));
+    this.initializeImageView();
+
+    // Side menu
+
+    //TEMPORARY CODE to test Chaos Games in GUI
+    this.initializeGameComponents();
+
+    this.initializeMainButtons();
+    this.initializeFractalButtons();
+    this.initializeSideMenu();
+
+    this.scene = new Scene(this.borderPane,1700,1000);
+  }
+
+  /**
+   * Initialize the main buttons for the GUI.
+   */
+  private void initializeMainButtons() {
+    this.startButton = new Button("Start");
+    startButton.setOnAction(event -> timeline.play());
+    this.stopButton = new Button("Stop");
+    stopButton.setOnAction(event -> timeline.stop());
+
+    this.newButton = new Button("New");
+
+    newButton.setOnAction(event ->{
+      this.canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+      chaosCanvas.clearCanvas();
+    });
+
+    this.clearButton = new Button("Clear");
+
+    clearButton.setOnAction(event -> {
+      getImageView().setImage(null);
+      setCurrentLine(0);
+    });
+
+    // Quit button
+    this.quitButton = new Button("Quit");
+    quitButton.setOnAction(event -> Platform.exit());
+
+  }
+
+  /**
+   * Initialize the components related to the chaos game itself.
+   */
+  private void initializeGameComponents() {
+    // Description
+    this.factory = new ChaosGameDescriptionFactory();
+    this.description = factory.getDescriptions().get(0);
+
+    this.chaosCanvas = new ChaosCanvas(100, 100, this.description.getMinCoords(),
+        this.description.getMaxCoords());
+    game = new ChaosGame(this.description, chaosCanvas);
+  }
+
+  /**
+   * Initialize components related to the image view and zoom function.
+   */
+  private void initializeImageView() {
+    // Image view
+    this.imageView = new ChaosGameImageView(this);
+    width = 1000;
+    height = 1000;
+    this.canvas = new Canvas(width, height);
+    //this.imageView.setImage(canvas.snapshot(null, null));
+
+    this.clearImageView();
+
+  }
+
+  /**
+   * Color the entire image view white.
+   */
+  private void clearImageView() {
+    GraphicsContext gc = canvas.getGraphicsContext2D();
+    gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+    imageView.setImage(null);
+  }
+
+  /**
+   * Initialize the buttons related to managing the fractals.
+   */
+  private void initializeFractalButtons() {
+
+    // Radio buttons for choosing fractal type
+    ToggleGroup group = new ToggleGroup();
+    this.sierpinskiRadioButton = new RadioButton("Sierpinski");
+    sierpinskiRadioButton.setToggleGroup(group);
+    sierpinskiRadioButton.setSelected(true);
+    this.barnsleyRadioButton = new RadioButton("Barnsley");
+    barnsleyRadioButton.setToggleGroup(group);
+    this.juliaRadioButton = new RadioButton("Julia");
+    juliaRadioButton.setToggleGroup(group);
+    this.improvedBarnsleyButton = new RadioButton("Improved Barnsley");
+    improvedBarnsleyButton.setToggleGroup(group);
+
+
+    // Set action for Sierpinski radio button.
+    sierpinskiRadioButton.setOnAction(event -> {
+      this.updateDescription(0);
+    });
+
+    // Set action for Barnsley radio button.
+    barnsleyRadioButton.setOnAction(event -> {
+      this.updateDescription(1);
+    });
+
+    // Set action for Julia radio button.
+    juliaRadioButton.setOnAction(event -> {
+      this.updateDescription(2);
+    });
+
+    improvedBarnsleyButton.setOnAction(event -> {
+      this.updateDescription(3);
+    });
+
+    // Load fractal file button
+    this.loadFractalFromFileButton = new Button("Load Fractal");
+    // Write fractal to file button
+    this.writeFractalToFileButton = new Button("Write to File");
+  }
+
+  /**
+   * Initialize the side menu.
+   */
+  private void initializeSideMenu() {
+    this.sideMenu = new VBox();
+    // Parameters
+    VBox parameterBox = new VBox();
+
+    // Step Count GUI
+    VBox stepCountBox = new VBox();
+    Label stepCountLabel = new Label("Step Count");
+    this.stepCountTextField = new TextField();
+    this.stepCountTextField.setTextFormatter(Formatter.getIntFormatter()); // Set formatter
+    stepCountTextField.setPrefHeight(5);
+    stepCountTextField.setPrefWidth(50);
+
+    stepCountBox.getChildren().addAll(stepCountLabel,stepCountTextField);
+
+    // Minimum Coordinates GUI
+    VBox minCoordinatesBox = new VBox();
+    Label minCoordinatesLabel = new Label("Min. Coordinates");
+    TextField minimumCoordinatesTextField = new TextField();
+    minimumCoordinatesTextField.setPrefHeight(5);
+    minimumCoordinatesTextField.setPrefWidth(50);
+    Button changeMinimumCoordinatesButton = new Button("Change Min. Coordinates");
+    minCoordinatesBox.getChildren().addAll(minCoordinatesLabel,
+        minimumCoordinatesTextField,changeMinimumCoordinatesButton);
+
+    // Maximum Coordinates GUI
+    VBox maxCoordinatesBox = new VBox();
+    Label maxCoordinatesLabel = new Label("Max Coordinates");
+    TextField maximumCoordinatesTextField = new TextField();
+    maximumCoordinatesTextField.setPrefHeight(5);
+    maximumCoordinatesTextField.setPrefWidth(50);
+    Button changeMaximumCoordinatesButton = new Button("Change Max Coordinates");
+    maxCoordinatesBox.getChildren().addAll(maxCoordinatesLabel,
+        maximumCoordinatesTextField,changeMaximumCoordinatesButton);
+
+    HBox colorBox = new HBox();
+    Label colorLabel = new Label("Use color");
+    this.colorCheckBox = new CheckBox();
+    Region colorRegion = new Region();
+    colorRegion.setMinWidth(30);
+    colorBox.getChildren().addAll(colorCheckBox, colorRegion, colorLabel);
+
+    Region separator1 = new Region();
+    separator1.setMinHeight(10);
+    Region separator2 = new Region();
+    separator2.setMinHeight(10);
+
+    // Fill parameter box
+    parameterBox.getChildren().addAll(stepCountBox, minCoordinatesBox, maxCoordinatesBox);
+    parameterBox.setPadding(new Insets(10));
+
+    // Add basic control buttons
+    sideMenu.getChildren().addAll(startButton,stopButton,newButton,clearButton);
+
+    // Add fractal radio buttons
+    sideMenu.getChildren().addAll(sierpinskiRadioButton, barnsleyRadioButton, juliaRadioButton,
+        improvedBarnsleyButton);
+
+    sideMenu.getChildren().addAll(separator1, colorBox, separator2);
+    this.initializeColorButtonHandler();
+
+    // Add parameter VBox
+    sideMenu.getChildren().add(parameterBox);
+
+    // Add file buttons
+    sideMenu.getChildren().addAll(loadFractalFromFileButton,writeFractalToFileButton);
+
+    // Add quit button
+    sideMenu.getChildren().add(quitButton);
+
+    // Add padding
+    sideMenu.setPadding(new Insets(10));
+
+    // Create split pane and button to toggle sidebar
+    this.sideMenuButton = new Button(">>");
+    this.initializeSideButtonHandler();
+    Region sideMenuButtonRegion = new Region();
+    sideMenuButtonRegion.setMinWidth(200);
+    HBox sideMenuButtonBox = new HBox();
+    sideMenuButtonBox.getChildren().addAll(sideMenuButtonRegion, sideMenuButton);
+
+    // The right VBox containing both the sidebar and the sidebar toggle button.
+    VBox rightVBox = new VBox();
+
+    rightVBox.getChildren().addAll(sideMenuButtonBox, sideMenu);
+    this.sideMenu.setStyle("-fx-background-color: lightgrey; -fx-background-radius: 3;");
+
+    this.borderPane = new BorderPane();
+    this.borderPane.setCenter(imageView);
+    this.borderPane.setRight(rightVBox);
+    imageView.setFocusTraversable(true);
+    rightVBox.setFocusTraversable(false);
+    borderPane.setFocusTraversable(false);
+
+  }
+
+  /**
+   * Initialise the side bar button handler, allowing the user
+   * to show or hide the right sidebar.
+   */
+  private void initializeSideButtonHandler() {
+    TranslateTransition openNav = new TranslateTransition(new Duration(350), sideMenu);
+    openNav.setToX(0);
+    TranslateTransition closeNav = new TranslateTransition(new Duration(350), sideMenu);
+
+    this.sideMenuButton.setOnAction(e -> {
+      if(sideMenu.getTranslateX() != 0){
+        this.sideMenuButton.setText(">>");
+        openNav.play();
+      } else {
+        closeNav.setToX(sideMenu.getWidth());
+        closeNav.play();
+        this.sideMenuButton.setText("<<");
+      }
+    });
+  }
+
+  /**
+   * Initialize the color button handler.
+   */
+  private void initializeColorButtonHandler() {
+    this.colorCheckBox.setOnAction(event -> {
+      game.setUseColor(colorCheckBox.isSelected());
+      this.clearImageView();
+      this.chaosCanvas.clearCanvas();
+    });
+  }
+
+  /**
+   * Get the chaos canvas of this GUI view.
+   *
+   * @return the canvas.
+   */
+  public ChaosCanvas getChaosCanvas() {
+    return this.chaosCanvas;
+  }
+
+  public void drawChaosGame(){
+    // Run the number of steps specified in text field, else 1000.
+    game.runSteps(!Objects.equals(this.stepCountTextField.getText(), "") ?
+        Integer.parseInt(this.stepCountTextField.getText()) : 1000);
+
+    // Convert the canvas to either an image with coloured pixels based on intensity, or just black and white.
+    ChaosCanvasToImageConverter converter = new ChaosCanvasToImageConverter(this.chaosCanvas, 
+        this.colorCheckBox.isSelected());
+    WritableImage image = converter.getImage();
+    this.canvas.getGraphicsContext2D().drawImage(image, 0, 0);
+    this.imageView.setImage(image);
+  }
+
+  public int getWidth(){
+    return this.width;
+  }
+  public int getHeight(){
+    return this.height;
+  }
+  public ImageView getImageView(){
+    return this.imageView;
+  }
+
+  public void setCurrentLine(int currentLine) {
+    this.currentLine = currentLine;
+  }
+
+  public void setImageViewFromImage(Image inputView) {
+    this.imageView.setImage(inputView);
+  }
+
+  /**
+   * Update the description of the chaos game.
+   * TODO: this method may need to be changed depending on how we implement the UI. Rename to update?
+   *
+   * @param index the index of the new description in the list of factory descriptions.
+   *
+   */
+  @Override
+  public void updateDescription(int index) {
+    timeline.stop();
+    this.chaosCanvas.clearCanvas();
+    this.canvas.getGraphicsContext2D().clearRect(0, 0, this.canvas.getGraphicsContext2D().
+        getCanvas().getWidth(), this.canvas.getGraphicsContext2D().getCanvas().getHeight());
+    this.clearImageView();
+    this.chaosCanvas.clearCanvas();
+
+    this.description = this.factory.getDescriptions().get(index); // Assuming the Sierpinski description is at index 0
+    this.chaosCanvas = new ChaosCanvas(1000, 1000, this.description.getMinCoords(),
+        this.description.getMaxCoords());
+    this.updateCanvas(this.chaosCanvas);
+    game = new ChaosGame(this.description, this.chaosCanvas);
+    game.setUseColor(this.colorCheckBox.isSelected());
+    //this.game.setDescription(description);
+  }
+
+  /**
+   * Update the canvas and set a new zoom factor for the image view based on the ratio
+   * between the old and new canvas heights.
+   *
+   * @param canvas the canvas to update with.
+   */
+  @Override
+  public void updateCanvas(ChaosCanvas canvas) {
+    float zoomRatio = (float) this.chaosCanvas.getHeight() / canvas.getHeight();
+    //this.imageView.fixedZoom(zoomRatio); // Set new zoom factor.
+    this.chaosCanvas = canvas;
+  }
+
+  /**
+   * Update which parts of the fractal are rendered and at what level of detail.
+   *
+   * @param zoomLevel the number of recursive zoom levels.
+   * @param centreX the x-coordinate of the centre of the image view.
+   * @param centreY the y-coordinate of the centre of the image view.
+   */
+  public void updateDetail(int zoomLevel, double centreX, double centreY) {
+    this.clearImageView();
+    this.chaosCanvas.clearCanvas();
+    this.chaosCanvas.updateCoords(centreX, centreY, zoomLevel);
+    this.game.setCurrentPoint(new Vector2D(centreX, centreY));
+  }
+
+  /**
+   * Update the observer based on changes to the chaos game.
+   * TODO: this method may need to be changed depending on how we implement the UI. The update method may need to be split.
+   *
+   * @param game the game this observer is monitoring.
+   */
+  @Override
+  public void update(ChaosGame game) {
+    //drawChaosGame();
+  }
+
+
+}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGuiView.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGuiView.java
deleted file mode 100644
index 8221f304e7a35397b695cba8993dbda578a92c35..0000000000000000000000000000000000000000
--- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameGuiView.java
+++ /dev/null
@@ -1,468 +0,0 @@
-package edu.ntnu.stud.chaosgame.view;
-
-import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
-import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
-import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
-import edu.ntnu.stud.chaosgame.model.generators.ChaosGameDescriptionFactory;
-
-import edu.ntnu.stud.chaosgame.model.transformations.AffineTransform2D;
-import javafx.animation.KeyFrame;
-import javafx.animation.Timeline;
-import javafx.application.Platform;
-import javafx.geometry.Insets;
-import javafx.scene.Scene;
-import javafx.scene.control.*;
-import javafx.scene.image.Image;
-import javafx.scene.image.ImageView;
-import javafx.scene.image.PixelWriter;
-import javafx.scene.image.WritableImage;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.VBox;
-import javafx.scene.paint.Color;
-import javafx.stage.Stage;
-import javafx.util.Duration;
-
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class ChaosGameGuiView implements ChaosGameObserver {
-  private int currentLine = 0;
-
-  /**
-   * The ChaosCanvas for this GUI.
-   */
-  private ChaosCanvas canvas;
-
-  /**
-   * The ChaosGameDescription.
-   */
-  private ChaosGameDescription description;
-
-  /**
-   * The ChaosGameDescriptionFactory.
-   */
-  private ChaosGameDescriptionFactory factory;
-
-  /**
-   * The AtomicReference for the ChaosGameDescription.
-   */
-  private AtomicReference<ChaosGameDescription> descriptionRef;
-
-  /**
-   * The PixelWriter for the GUI.
-   */
-  private PixelWriter pixelWriter;
-
-  /**
-   * The ImageView for the GUI.
-   */
-  private ChaosGameImageView imageView;
-
-  /**
-   * The Scene for the GUI.
-   */
-  private Scene scene;
-
-  /**
-   * The width and height of the GUI.
-   */
-  private int width;
-  private int height;
-
-  /**
-   * The ChaosGame for this GUI.
-   */
-  private ChaosGame game;
-
-  /**
-   * The Timeline for the GUI.
-   */
-  private Timeline timeline;
-
-  /**
-   * The BorderPane for the GUI.
-   */
-  private BorderPane borderPane;
-
-  /**
-   * The side menu for the GUI.
-   */
-  private VBox sideMenu;
-
-  /**
-   * The start, stop, new, clear, and quit buttons for the GUI.
-   */
-  private Button startButton;
-  private Button stopButton;
-  private Button newButton;
-  private Button clearButton;
-  private Button quitButton;
-
-  /**
-   * The load fractal from file and write fractal to file buttons for the GUI.
-   */
-  private Button loadFractalFromFileButton;
-  private Button writeFractalToFileButton;
-
-  /**
-   * The radio buttons for the fractal type for the GUI.
-   */
-  private RadioButton sierpinskiRadioButton;
-  private RadioButton barnsleyRadioButton;
-  private RadioButton juliaRadioButton;
-  private RadioButton improvedBarnsleyButton;
-
-  private final int resolutionHeight = 1000;
-  private final int resolutionWidth = 1000;
-
-
-  public ChaosGameGuiView(Stage primaryStage) throws IOException {
-
-    this.initializeComponents();
-
-    primaryStage.setTitle("Fractal Chaos Game");
-    primaryStage.setScene(scene);
-    primaryStage.setOnShown(event -> this.imageView.requestFocus());
-    primaryStage.show();
-
-  }
-
-  /**
-   * Initialize the components of the GUI.
-   */
-  private void initializeComponents() {
-
-    // Timeline
-    this.timeline = new Timeline(new KeyFrame(Duration.seconds(0.05), event -> this.drawChaosGame()));
-    this.initializeImageView();
-    this.timeline.setCycleCount(Timeline.INDEFINITE);
-
-
-    // Side menu
-
-    //TEMPORARY CODE to test Chaos Games in GUI
-    this.initializeGameComponents();
-
-    this.initializeMainButtons();
-    this.initializeFractalButtons();
-    this.initializeSideMenu();
-
-    this.scene = new Scene(this.borderPane,1700,1000);
-  }
-
-  /**
-   * Initialize the main buttons for the GUI.
-   */
-  private void initializeMainButtons() {
-    this.startButton = new Button("Start");
-    startButton.setOnAction(event -> timeline.play());
-    this.stopButton = new Button("Stop");
-    stopButton.setOnAction(event -> timeline.stop());
-
-    this.newButton = new Button("New");
-
-    newButton.setOnAction(event ->{
-      WritableImage newWritableImage = new WritableImage(width, height);
-      setPixelWriter(newWritableImage.getPixelWriter());
-      setImageViewFromImage(newWritableImage);
-      canvas.clearCanvas();
-    });
-
-    this.clearButton = new Button("Clear");
-
-    clearButton.setOnAction(event -> {
-      getImageView().setImage(null);
-      setCurrentLine(0);
-    });
-
-    // Quit button
-    this.quitButton = new Button("Quit");
-    quitButton.setOnAction(event -> Platform.exit());
-
-  }
-
-  /**
-   * Initialize the components related to the chaos game itself.
-   */
-  private void initializeGameComponents() {
-    // Description
-    this.factory = new ChaosGameDescriptionFactory();
-    this.descriptionRef = new AtomicReference<>(factory.getDescriptions().get(0));
-    this.description = descriptionRef.get();
-
-    this.canvas = new ChaosCanvas(resolutionWidth, resolutionHeight, descriptionRef.get().getMinCoords(), descriptionRef.get().getMaxCoords());
-    game = new ChaosGame(this.description, canvas);
-  }
-
-  /**
-   * Initialize components related to the image view and zoom function.
-   */
-  private void initializeImageView() {
-    // Image view
-    this.imageView = new ChaosGameImageView(this);
-    width = resolutionWidth;
-    height = resolutionHeight;
-    WritableImage writableImage = new WritableImage(width, height);
-    pixelWriter = writableImage.getPixelWriter();
-    this.imageView.setImage(writableImage);
-    this.imageView.setFitHeight(1000);
-    this.imageView.setFitWidth(1000);
-
-    this.fillImageView();
-    /*imageView.setOnScroll(event -> {
-      double zoomFactor = 1.05; // This is the zoom factor per scroll
-      double movement = event.getDeltaY();
-
-      imageView.setTranslateX(imageView.getTranslateX() - event.getDeltaX());
-      imageView.setTranslateY(imageView.getTranslateY() - event.getDeltaY());
-
-      if (movement > 0) {
-        imageView.setScaleX(imageView.getScaleX() * zoomFactor);
-        imageView.setScaleY(imageView.getScaleY() * zoomFactor);
-      } else {
-        imageView.setScaleX(imageView.getScaleX() / zoomFactor);
-        imageView.setScaleY(imageView.getScaleY() / zoomFactor);
-      }
-
-      // Limit the zoom so the scale doesn't become too big or too small
-      imageView.setScaleX(Math.max(imageView.getScaleX(), 1.0));
-      imageView.setScaleY(Math.max(imageView.getScaleY(), 1.0));
-      imageView.setScaleX(Math.min(imageView.getScaleX(), 10.0)); // max scale
-      imageView.setScaleY(Math.min(imageView.getScaleY(), 10.0)); // max scale
-
-      event.consume(); // consume the event so it doesn't propagate further
-    });*/
-
-  }
-
-  /**
-   * Color the entire image view white to facilitate scrolling.
-   */
-  private void fillImageView() {
-    // Color the image white
-    for (int y = 0; y < height; y++) {
-      for (int x = 0; x < width; x++) {
-        pixelWriter.setColor(x, y, Color.WHITE);
-      }
-    }
-
-  }
-
-  /**
-   * Initialize the buttons related to managing the fractals.
-   */
-  private void initializeFractalButtons() {
-
-    // Radio buttons for choosing fractal type
-    ToggleGroup group = new ToggleGroup();
-    this.sierpinskiRadioButton = new RadioButton("Sierpinski");
-    sierpinskiRadioButton.setToggleGroup(group);
-    sierpinskiRadioButton.setSelected(true);
-    this.barnsleyRadioButton = new RadioButton("Barnsley");
-    barnsleyRadioButton.setToggleGroup(group);
-    this.juliaRadioButton = new RadioButton("Julia");
-    juliaRadioButton.setToggleGroup(group);
-    this.improvedBarnsleyButton = new RadioButton("Improved Barnsley");
-    improvedBarnsleyButton.setToggleGroup(group);
-
-
-    AtomicReference<ChaosCanvas> canvasRef = new AtomicReference<>(canvas);
-
-    // Set action for Sierpinski radio button.
-    sierpinskiRadioButton.setOnAction(event -> {
-      timeline.stop();
-      //canvasRef.get().clearCanvas();
-      WritableImage newWritableImage = new WritableImage(width, height);
-      setPixelWriter(newWritableImage.getPixelWriter());
-      setImageViewFromImage(newWritableImage);
-      this.fillImageView();
-      //canvas.clearCanvas();
-
-      this.descriptionRef.set(factory.getDescriptions().get(0)); // Assuming the Sierpinski description is at index 0
-      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
-      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
-    });
-
-    // Set action for Barnsley radio button.
-    barnsleyRadioButton.setOnAction(event -> {
-      timeline.stop();
-      //canvasRef.get().clearCanvas();
-      WritableImage newWritableImage = new WritableImage(width, height);
-      setPixelWriter(newWritableImage.getPixelWriter());
-      setImageViewFromImage(newWritableImage);
-      this.fillImageView();
-      //canvas.clearCanvas();
-      this.descriptionRef.set(factory.getDescriptions().get(1)); // Assuming the Sierpinski description is at index 0
-      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
-      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
-    });
-
-    // Set action for Julia radio button.
-    juliaRadioButton.setOnAction(event -> {
-      timeline.stop();
-      //canvasRef.get().clearCanvas();
-      WritableImage newWritableImage = new WritableImage(width, height);
-      setPixelWriter(newWritableImage.getPixelWriter());
-      setImageViewFromImage(newWritableImage);
-      this.fillImageView();
-      //canvas.clearCanvas();
-      this.descriptionRef.set(factory.getDescriptions().get(2)); // Assuming the Sierpinski description is at index 0
-      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
-      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
-    });
-
-    improvedBarnsleyButton.setOnAction(event -> {
-      timeline.stop();
-      //canvasRef.get().clearCanvas();
-      WritableImage newWritableImage = new WritableImage(width, height);
-      setPixelWriter(newWritableImage.getPixelWriter());
-      setImageViewFromImage(newWritableImage);
-      this.fillImageView();
-      //canvas.clearCanvas();
-
-      // Test
-      this.descriptionRef.set(factory.getDescriptions().get(3)); // Assuming the Sierpinski description is at index 0
-      this.description = factory.getDescriptions().get(3);
-
-      for (int i = 0; i < this.description.getTransforms().size(); i++) {
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA00());
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA01());
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA10());
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getMatrix().getA11());
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getVector().getX0());
-        System.out.println(((AffineTransform2D) this.description.getTransforms().get(i)).getVector().getX1());
-      }
-
-
-      canvasRef.set(new ChaosCanvas(resolutionWidth, resolutionHeight, this.descriptionRef.get().getMinCoords(), this.descriptionRef.get().getMaxCoords()));
-      game = new ChaosGame(this.descriptionRef.get(), canvasRef.get());
-    });
-
-    // Load fractal file button
-    this.loadFractalFromFileButton = new Button("Load Fractal");
-    // Write fractal to file button
-    this.writeFractalToFileButton = new Button("Write to File");
-  }
-
-  /**
-   * Initialize the side menu.
-   */
-  private void initializeSideMenu() {
-    this.sideMenu = new VBox();
-    // Parameters
-    VBox parameterBox = new VBox();
-    // Step Count GUI
-    VBox stepCountBox = new VBox();
-    Label stepCountLabel = new Label("Step Count");
-    TextArea stepCountTextArea = new TextArea();
-    stepCountTextArea.setPrefHeight(5);
-    stepCountTextArea.setPrefWidth(50);
-    Button changeStepCountButton = new Button("Change Step Count");
-    stepCountBox.getChildren().addAll(stepCountLabel,stepCountTextArea, changeStepCountButton);
-    // Minimum Coordinates GUI
-    VBox minCoordinatesBox = new VBox();
-    Label minCoordinatesLabel = new Label("Min. Coordinates");
-    TextArea minimumCoordinatesTextArea = new TextArea();
-    minimumCoordinatesTextArea.setPrefHeight(5);
-    minimumCoordinatesTextArea.setPrefWidth(50);
-    Button changeMinimumCoordinatesButton = new Button("Change Min. Coordinates");
-    minCoordinatesBox.getChildren().addAll(minCoordinatesLabel,minimumCoordinatesTextArea,changeMinimumCoordinatesButton);
-    // Maximum Coordinates GUI
-    VBox maxCoordinatesBox = new VBox();
-    Label maxCoordinatesLabel = new Label("Max Coordinates");
-    TextArea maximumCoordinatesTextArea = new TextArea();
-    maximumCoordinatesTextArea.setPrefHeight(5);
-    maximumCoordinatesTextArea.setPrefWidth(50);
-    Button changeMaximumCoordinatesButton = new Button("Change Max Coordinates");
-    maxCoordinatesBox.getChildren().addAll(maxCoordinatesLabel,maximumCoordinatesTextArea,changeMaximumCoordinatesButton);
-    // Fill parameter box
-    parameterBox.getChildren().addAll(stepCountBox, minCoordinatesBox, maxCoordinatesBox);
-    parameterBox.setPadding(new Insets(10));
-
-    // Add basic control buttons
-    sideMenu.getChildren().addAll(startButton,stopButton,newButton,clearButton);
-    // Add fractal radio buttons
-    sideMenu.getChildren().addAll(sierpinskiRadioButton, barnsleyRadioButton, juliaRadioButton, improvedBarnsleyButton);
-    // Add parameter VBox
-    sideMenu.getChildren().add(parameterBox);
-    // Add file buttons
-    sideMenu.getChildren().addAll(loadFractalFromFileButton,writeFractalToFileButton);
-    // Add quit button
-    sideMenu.getChildren().add(quitButton);
-    // Add padding
-    sideMenu.setPadding(new Insets(10));
-
-    this.borderPane = new BorderPane();
-    this.borderPane.setCenter(imageView);
-    this.borderPane.setRight(sideMenu);
-    imageView.setFocusTraversable(true);
-    sideMenu.setFocusTraversable(false);
-    borderPane.setFocusTraversable(false);
-
-  }
-
-  public void drawChaosGame(){
-    ChaosCanvas canvas = game.getCanvas();
-
-
-    game.runSteps(100000);
-
-    // Test implementation for drawing fractals
-    int[][] betaArray = canvas.getCanvasArray();
-    for (int i = 0; i < canvas.getWidth(); i++) {
-      for (int j = 0; j < canvas.getHeight(); j++) {
-        if (betaArray[i][j] == 1) {
-          pixelWriter.setColor(j,i,Color.BLACK);
-        }
-      }
-
-    }
-
-  }
-
-  public int getWidth(){
-    return this.width;
-  }
-  public int getHeight(){
-    return this.height;
-  }
-  public ImageView getImageView(){
-    return this.imageView;
-  }
-
-  public void setCurrentLine(int currentLine) {
-    this.currentLine = currentLine;
-  }
-
-  public void setPixelWriter(PixelWriter pixelWriter) {
-    this.pixelWriter = pixelWriter;
-  }
-
-  public void setImageViewFromImage(Image inputView) {
-    this.imageView.setImage(inputView);
-  }
-
-  /**
-   * Update the description of the chaos game.
-   * TODO: this method may need to be changed depending on how we implement the UI.
-   *
-   * @param description the description.
-   */
-  @Override
-  public void updateDescription(ChaosGameDescription description) {
-    this.game.setDescription(description);
-  }
-
-  /**
-   * Update the observer based on changes to the chaos game.
-   * TODO: this method may need to be changed depending on how we implement the UI. The update method may need to be split.
-   *
-   * @param game the game this observer is monitoring.
-   */
-  @Override
-  public void update(ChaosGame game) {
-    //drawChaosGame();
-  }
-
-
-}
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java
index 402051123b9d4d63d54b51e178b1c7172da97fea..e956a4f580081ca591abdba502bf203d5e644846 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameImageView.java
@@ -11,34 +11,70 @@ import javafx.scene.transform.Affine;
  */
 public class ChaosGameImageView extends ImageView {
 
-  private final ChaosGameGuiView controller;
+  /**
+   * The controller for this class: a chaos game GUI view.
+   */
+  private final ChaosGameGui controller;
+
+  /**
+   * Affine initialised to the identity matrix.
+   */
   private final Affine transform = new Affine();
 
-  private double lastCentreX;
-  private double lastCentreY;
+  private double centreX;
+  private double centreY;
+
+  /**
+   * The starting x and y positions upon a mouse event.
+   */
   private double startX;
   private double startY;
 
-  public ChaosGameImageView(ChaosGameGuiView controller) {
-    this.setOnScroll(this::zoom);
+  /**
+   * The factor representing the magnitude of the current zoom.
+   */
+  private double zoomFactor = 1.0;
+
+  /**
+   * Integer representing how many recursive levels of QuadTrees
+   * are required to get to the current zoom magnitude.
+   */
+  private int zoomLevel = 0;
+
+
+
+  /**
+   * Constructor for the ChaosGameImageView.
+   *
+   * @param controller the GUI which controls this image view.
+   */
+  public ChaosGameImageView(ChaosGameGui controller) {
+    this.setOnScroll(this::userZoom);
     this.setOnMousePressed(this::mousePressed);
     this.setOnMouseDragged(this::mouseDragged);
-    this.setOnMouseReleased(this::mouseReleased);
+    //this.setOnMouseReleased(this::mouseReleased);
     this.getTransforms().add(transform);
     this.controller = controller;
-    this.lastCentreX = (float) controller.getWidth() / 2;
-    this.lastCentreY = (float) -controller.getHeight() / 2;
+    //this.lastCentreX = (float) controller.getWidth() / 2;
+    //this.lastCentreY = (float) -controller.getHeight() / 2;
 
     //this.setStyle("-fx-background-color: white;");
   }
 
+  /**
+   * Get the current zoom level.
+   *
+   * @return the integer representing the zoom level.
+   */
+  public int getZoomLevel() {return this.zoomLevel; }
+
   /**
    * Zooms the image view in or out based on the scroll event.
    *
    * @param event the event.
    */
-  private synchronized void zoom(ScrollEvent event) {
-    double zoomFactor = event.getDeltaY() > 0 ? 1.20 : 1 / 1.05;
+  private synchronized void userZoom(ScrollEvent event) {
+    double newZoomFactor = event.getDeltaY() > 0 ? 1.10 : 1 / 1.05;
     try {
       // Get the old values
       double oldScaleX = transform.getMxx();
@@ -47,8 +83,8 @@ public class ChaosGameImageView extends ImageView {
       double oldTranslateY = transform.getTy();
 
       // Compute the new values
-      double newScaleX = oldScaleX * zoomFactor;
-      double newScaleY = oldScaleY * zoomFactor;
+      double newScaleX = oldScaleX * newZoomFactor;
+      double newScaleY = oldScaleY * newZoomFactor;
       double newTranslateX = oldTranslateX - (event.getX() * (newScaleX - oldScaleX));
       double newTranslateY = oldTranslateY - (event.getY() * (newScaleY - oldScaleY));
 
@@ -57,16 +93,63 @@ public class ChaosGameImageView extends ImageView {
       transform.setMyy(newScaleY);
       transform.setTx(newTranslateX);
       transform.setTy(newTranslateY);
+
+      this.zoomFactor *= newZoomFactor;
+      this.zoomLevel = (int) (Math.log(this.zoomFactor) / Math.log(4)); // Update zoom level.
+
+
     } catch (Exception e) {
       PopupManager.displayError("Zoom error", e.getMessage());
     }
   }
 
+  public void fixedZoom(double newZoomFactor) {
+    try {
+      // Get the old values
+      double oldScaleX = transform.getMxx();
+      double oldScaleY = transform.getMyy();
+      double oldTranslateX = transform.getTx();
+      double oldTranslateY = transform.getTy();
+
+      // Compute the new values
+      double newScaleX = oldScaleX * newZoomFactor;
+      double newScaleY = oldScaleY * newZoomFactor;
+      double newTranslateX = oldTranslateX - (this.getWidth() / 2 * (newScaleX - oldScaleX));
+      double newTranslateY = oldTranslateY - (this.getHeight() / 2 * (newScaleY - oldScaleY));
+
+      // Update the transform
+      transform.setMxx(newScaleX);
+      transform.setMyy(newScaleY);
+      transform.setTx(newTranslateX);
+      transform.setTy(newTranslateY);
+
+      this.zoomFactor *= newZoomFactor;
+      this.zoomLevel = (int) (Math.log(this.zoomFactor) / Math.log(4)); // Update zoom level.
+
+    } catch (Exception e) {
+      PopupManager.displayError("Zoom error", e.getMessage());
+    }
+  }
+// TODO: remove if unused
+  private void updateController() {
+    this.controller.updateDetail(this.zoomLevel, this.centreX, this.centreY);
+  }
+
+  /**
+   * Gets mouse cursor position data when mouse button is pressed.
+   *
+   * @param event the mouse event.
+   */
   private synchronized void mousePressed(javafx.scene.input.MouseEvent event) {
     startX = event.getX();
     startY = event.getY();
   }
 
+  /**
+   * Drags the image view based on the mouse cursor position.
+   *
+   * @param event the mouse event.
+   */
   private synchronized void mouseDragged(javafx.scene.input.MouseEvent event) {
     double deltaX = event.getX() - startX;
     double deltaY = event.getY() - startY;
@@ -75,13 +158,14 @@ public class ChaosGameImageView extends ImageView {
     transform.setTy(transform.getTy() + deltaY);
   }
 
-  private synchronized void mouseReleased(javafx.scene.input.MouseEvent event) {
-    double deltaX = event.getX() - startX;
-    double deltaY = event.getY() - startY;
-
+  public int getWidth() {
+    return (int) this.getImage().getWidth();
+  }
 
-    lastCentreX += deltaX;
-    lastCentreY += deltaY;
+  public int getHeight() {
+    return (int) this.getImage().getHeight();
   }
 
+  public Affine getTransform() {return this.transform; }
+
 }
diff --git a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java
index 40b7fcab45df00e2e4ace24e4aad84eefb6cf313..5eb1f0888f1a289801c797e4f626a307b1857adc 100644
--- a/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java
+++ b/src/main/java/edu/ntnu/stud/chaosgame/view/ChaosGameObserver.java
@@ -1,6 +1,7 @@
 package edu.ntnu.stud.chaosgame.view;
 
 import edu.ntnu.stud.chaosgame.controller.game.ChaosGame;
+import edu.ntnu.stud.chaosgame.controller.game.ChaosCanvas;
 import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
 
 /**
@@ -8,23 +9,21 @@ import edu.ntnu.stud.chaosgame.controller.game.ChaosGameDescription;
  * TODO: Do we want to have separate update methods for the canvas and description or just one for the whole game? (likely the latter)
  */
 public interface ChaosGameObserver {
-// TODO: Create interface
-
 
   /**
-   * Perform update of the ChaosCanvas.
+   * Perform update of the ChaosGameDescription.
    *
-   * @param canvas the canvas.
+   * @param index the description's index in a list of descriptions.
    */
-  //void updateCanvas(Chaosanvas canvas);
+  //void updateDescription(ChaosGameDescription description);
+  void updateDescription(int index);
 
   /**
-   * Perform update of the ChaosGameDescription.
+   * Update the ChaosCanvas.
    *
-   * @param description the description.
+   * @param canvas the canvas to update with.
    */
-  //void updateDescription(ChaosGameDescription description);
-  void updateDescription(ChaosGameDescription description);
+  void updateCanvas(ChaosCanvas canvas);
 
   /**
    * Update the observer based on changes to the chaos game.
diff --git a/src/test/java/edu/ntnu/stud/chaosgame/game/ChaosCanvasTest.java b/src/test/java/edu/ntnu/stud/chaosgame/game/ChaosCanvasTest.java
index 19a48136b164bbed5d7a6ed0d25afbd8f43d4ea0..9f7e32f60b6de28e6e3b103826e2dbb6e77f9a9f 100644
--- a/src/test/java/edu/ntnu/stud/chaosgame/game/ChaosCanvasTest.java
+++ b/src/test/java/edu/ntnu/stud/chaosgame/game/ChaosCanvasTest.java
@@ -56,7 +56,7 @@ public class ChaosCanvasTest {
   @Test
   void shouldPutPixel() {
     Vector2D point = new Vector2D(0.2, 0.3);
-    this.canvas.putPixel(point);
+    this.canvas.putPixel(point, false);
 
     // Test whether new point was added
     Assertions.assertEquals(this.canvas.getPixel(point), 1);
@@ -65,6 +65,24 @@ public class ChaosCanvasTest {
     Assertions.assertEquals(this.canvas.getPixel(new Vector2D(0.1, 0.4)), 0);
   }
 
+  /**
+   * Tests whether putting pixels in the intensity array works as expected, incrementing
+   * by one for each time the pixel is visited.
+   */
+  @Test
+  void shouldPutPixelAndIntensity() {
+    Vector2D point = new Vector2D(0.2, 0.3);
+    this.canvas.putPixel(point, true);
+    this.canvas.putPixel(point, true);
+
+    // Test whether new point was added
+    Assertions.assertEquals(this.canvas.getPixel(point), 1);
+    Assertions.assertEquals(this.canvas.getIntensityPixel(point), 2);
+
+    // Ensure another, arbitrary point was not also added
+    Assertions.assertEquals(this.canvas.getPixel(new Vector2D(0.1, 0.4)), 0);
+  }
+
   /**
    * Test whether clearing the canvas works as expected.
    */
@@ -73,7 +91,7 @@ public class ChaosCanvasTest {
 
     // Put pixels throughout a part of the canvas.
     for (int i = 1; i < 101; i++) {
-      this.canvas.putPixel(new Vector2D( 1.0 / i, 1.0 / i));
+      this.canvas.putPixel(new Vector2D( 1.0 / i, 1.0 / i), false);
 
       // Check that the of the points where a pixel was added, are not equal to 0.
       Assertions.assertNotEquals(0, this.canvas.getPixel(new Vector2D(1.0 / i, 1.0 / i)));