diff --git a/pom.xml b/pom.xml
index ef13866d842a5d564ae2dc676d6415db6f852b64..81ce2385023cf40136ee4c7709d7d1838caf6b8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,7 @@
                 <artifactId>javafx-maven-plugin</artifactId>
                 <version>0.0.8</version>
                 <configuration>
-                    <mainClass>edu.ntnu.idatt2003.Main</mainClass>
+                    <mainClass>edu.ntnu.idatt2003.launcher.Main</mainClass>
                 </configuration>
             </plugin>
             <plugin>
diff --git a/src/main/java/edu/ntnu/idatt2003/controller/GameStateManager.java b/src/main/java/edu/ntnu/idatt2003/controller/GameStateManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..28793ec3c787c6fe4eea66d34e97bf89f9385815
--- /dev/null
+++ b/src/main/java/edu/ntnu/idatt2003/controller/GameStateManager.java
@@ -0,0 +1,76 @@
+package edu.ntnu.idatt2003.controller;
+
+import edu.ntnu.idatt2003.model.ChaosGame;
+import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Manages the game state by providing methods to save and load the game state from a file.
+ */
+public class GameStateManager {
+
+  private static final String SERIALIZED_GAME_PATH = "src/main/resources/savedGameState.ser";
+  private static final Logger LOGGER = Logger.getLogger(GameStateManager.class.getName());
+
+  /**
+   * Saves the current game state to a serialized file.
+   *
+   * @param game The current game state to be saved.
+   */
+  public static void saveGameState(ChaosGame game) {
+    LOGGER.log(Level.INFO, "Saving game state.");
+    try (ObjectOutputStream oos = new ObjectOutputStream(
+        new FileOutputStream(SERIALIZED_GAME_PATH))) {
+      oos.writeObject(game);
+      LOGGER.log(Level.INFO, "Game state saved successfully in {0}", SERIALIZED_GAME_PATH);
+    } catch (IOException e) {
+      LOGGER.log(Level.WARNING, "Failed to save game state.", e);
+    }
+  }
+
+  /**
+   * Loads the game state from a serialized file. If the file does not exist or loading fails,
+   * a new game state is created.
+   *
+   * @return The loaded game state, or a new game state if the file does not exist or loading fails.
+   */
+  public static ChaosGame loadGameState() {
+    LOGGER.log(Level.INFO, "Loading game state.");
+    File file = new File(SERIALIZED_GAME_PATH);
+    if (file.exists()) {
+      try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
+        ChaosGame loadedGame = (ChaosGame) ois.readObject();
+        LOGGER.log(Level.INFO, "Game state loaded successfully.");
+        return loadedGame;
+      } catch (IOException | ClassNotFoundException e) {
+        LOGGER.log(Level.WARNING, "Failed to load game state. Creating new game.", e);
+      }
+    } else {
+      LOGGER.log(Level.WARNING, "No saved game state found. Creating new game.");
+    }
+    return createNewGame();
+  }
+
+  /**
+   * Creates a new game state with default settings.
+   *
+   * @return The newly created game state.
+   */
+  private static ChaosGame createNewGame() {
+    ChaosGame newGame = new ChaosGame(
+        ChaosGameDescriptionFactory.get(
+            ChaosGameDescriptionFactory.DescriptionTypeEnum.SIERPINSKI_TRIANGLE),
+        650, 650);
+    newGame.setDescriptionName(
+        ChaosGameDescriptionFactory.DescriptionTypeEnum.SIERPINSKI_TRIANGLE.toString());
+    return newGame;
+  }
+}
+
diff --git a/src/main/java/edu/ntnu/idatt2003/controller/MainPageController.java b/src/main/java/edu/ntnu/idatt2003/controller/MainPageController.java
index 5de111797fdbf1e262b35d513a203870f47b8990..d1785753c7312f9337f3b48a278e89fa6fa6a23b 100644
--- a/src/main/java/edu/ntnu/idatt2003/controller/MainPageController.java
+++ b/src/main/java/edu/ntnu/idatt2003/controller/MainPageController.java
@@ -1,35 +1,27 @@
 package edu.ntnu.idatt2003.controller;
 
-import edu.ntnu.idatt2003.model.AffineTransform2D;
 import edu.ntnu.idatt2003.model.ChaosGame;
 import edu.ntnu.idatt2003.model.ChaosGameDescription;
 import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory;
 import edu.ntnu.idatt2003.model.ChaosGameFileHandler;
 import edu.ntnu.idatt2003.model.Complex;
 import edu.ntnu.idatt2003.model.JuliaTransform;
-import edu.ntnu.idatt2003.model.Matrix2x2;
 import edu.ntnu.idatt2003.model.Transform2D;
 import edu.ntnu.idatt2003.model.Vector2d;
+import edu.ntnu.idatt2003.utils.LoggerUtil;
+import edu.ntnu.idatt2003.utils.TransformationParser;
 import edu.ntnu.idatt2003.view.MainPageView;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.InputMismatchException;
 import java.util.List;
-import java.util.logging.FileHandler;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.logging.SimpleFormatter;
-import java.util.stream.DoubleStream;
 import java.util.stream.Stream;
 
 /**
@@ -43,22 +35,8 @@ public class MainPageController {
   private final List<String> customFractalNames;
   private boolean addingCustomFractal;
   private static final String FRACTAL_PATH = "src/main/resources/fractals/";
-  private static final String SERIALIZED_GAME_PATH = "src/main/resources/savedGameState.ser";
-  private static final Logger LOGGER = Logger.getLogger(MainPageController.class.getName());
+  private static final Logger LOGGER = LoggerUtil.setupLogger(MainPageController.class.getName());
 
-  static {
-    try {
-      new File("logs").mkdirs();
-
-      FileHandler fileHandler = new FileHandler("logs/application.log", false);
-      fileHandler.setFormatter(new SimpleFormatter());
-      fileHandler.setLevel(Level.WARNING);
-      LOGGER.addHandler(fileHandler);
-      LOGGER.setLevel(Level.ALL);
-    } catch (IOException e) {
-      e.printStackTrace();
-    }
-  }
 
   /**
    * The constructor for the MainPageController class.
@@ -66,13 +44,13 @@ public class MainPageController {
    * and renders the view.
    */
   public MainPageController() {
-    this.game = loadGameState();
+    this.game = GameStateManager.loadGameState();
     this.view = new MainPageView(this);
     this.game.registerObserver(view);
     this.customFractalNames = new ArrayList<>(getAllCustomFractalsNames());
     this.addingCustomFractal = false;
     this.view.render();
-    Runtime.getRuntime().addShutdownHook(new Thread(this::saveGameState));
+    Runtime.getRuntime().addShutdownHook(new Thread(() -> GameStateManager.saveGameState(game)));
     LOGGER.log(Level.INFO, "MainPageController initialized successfully.");
   }
 
@@ -130,49 +108,6 @@ public class MainPageController {
     return addingCustomFractal;
   }
 
-  /**
-   * Get the list of coordinate-arrays of the game.
-   *
-   * @return the list of coordinate-arrays of the game.
-   */
-  public List<double[]> getTransformList() {
-    if (fractalIsJulia()) {
-      return getTransformListJulia();
-    } else {
-      return getTransformListAffine();
-    }
-  }
-
-  private List<double[]> getTransformListJulia() {
-    List<double[]> transformList = new ArrayList<>();
-    transformList.add(((JuliaTransform) game.getTransformList().get(0)).getPointAsList());
-    return transformList;
-  }
-
-  private List<double[]> getTransformListAffine() {
-    List<double[]> transformList = new ArrayList<>();
-    for (Transform2D transform : game.getTransformList()) {
-      transformList.add(DoubleStream.concat(DoubleStream.of(((AffineTransform2D) transform)
-              .getMatrixCoordsList()), DoubleStream.of(((AffineTransform2D) transform)
-              .getVectorCoordsList())).toArray());
-    }
-    return transformList;
-  }
-
-  /**
-   * Check if the current fractal is a Julia fractal. If it is,
-   * return true and false otherwise.
-   *
-   * @return true if the fractal is a Julia fractal, false otherwise.
-   */
-  public boolean fractalIsJulia() {
-    try {
-      return game.getTransformList().get(0) instanceof JuliaTransform;
-    } catch (IndexOutOfBoundsException e) {
-      return false;
-    }
-  }
-
   /**
    * Run the chaos game simulation for the specified number of steps. If
    * the number of steps is negative, the canvas will be cleared.
@@ -211,43 +146,20 @@ public class MainPageController {
    */
   public void uploadFile(File file) {
     LOGGER.log(Level.INFO, "Uploading file: {0}", file.getName());
-    if (validateFile(file)
-            && (!Files.exists(Path.of(FRACTAL_PATH + file.getName()))
-            || view.askConfirmation("File already exists. Do you want to overwrite it?"))) {
-      storeFile(file);
-      LOGGER.log(Level.INFO, "File {0} uploaded successfully.", file.getName());
-      view.showAlert("File " + file.getName() + " uploaded successfully.");
-    }
-  }
-
-  private boolean validateFile(File file) {
-    try {
-      ChaosGameFileHandler.readFromFile(file);
-      return true;
-    } catch (InputMismatchException | FileNotFoundException e) {
-      view.showAlert(e.getMessage());
-      LOGGER.log(Level.WARNING, "Error uploading file. File was not uploaded.");
-      return false;
+    if (ChaosGameFileHandler.validateFile(file)
+        && (!Files.exists(Path.of(ChaosGameFileHandler.FRACTAL_PATH + file.getName()))
+        || view.askConfirmation("File already exists. Do you want to overwrite it?"))) {
+      try {
+        ChaosGameFileHandler.storeFile(file);
+        LOGGER.log(Level.INFO, "File {0} uploaded successfully.", file.getName());
+        view.showAlert("File " + file.getName() + " uploaded successfully.");
+      } catch (IOException e) {
+        view.showAlert("Error storing file. Please try again.");
+        LOGGER.log(Level.WARNING, "Error storing file. File was not stored.");
+      }
     }
   }
 
-  /**
-   * Stores the file in the resources/transformations folder of the project.
-   *
-   * @param file The file to store.
-   */
-  private void storeFile(File file) {
-    try {
-      String projectPath = System.getProperty("user.dir");
-      String destinationPath = projectPath + File.separator
-              + FRACTAL_PATH + file.getName();
-      Files.copy(file.toPath(), Path.of(destinationPath), StandardCopyOption.REPLACE_EXISTING);
-      LOGGER.log(Level.INFO, "File stored successfully in {0}", destinationPath);
-    } catch (IOException e) {
-      view.showAlert("Error storing file. Please try again.");
-      LOGGER.log(Level.WARNING, "Error storing file. File was not stored.");
-    }
-  }
 
   /**
    * Change the fractal-type of the chaos game.
@@ -285,47 +197,6 @@ public class MainPageController {
     }
   }
 
-  private void saveGameState() {
-    LOGGER.log(Level.INFO, "Saving game state.");
-    game.removeObserver(view);
-    try (ObjectOutputStream oos = new ObjectOutputStream(
-            new FileOutputStream(SERIALIZED_GAME_PATH))) {
-      oos.writeObject(game);
-      LOGGER.log(Level.INFO, "Game state saved successfully in {0}", SERIALIZED_GAME_PATH);
-    } catch (IOException e) {
-      LOGGER.log(Level.WARNING, "Failed to save game state. Next time the application is"
-              + " started, the game will be launched in same game state as this time.");
-    }
-  }
-
-  /**
-   * Load the game state from the serialized file to restore progress.
-   * If the file does not exist, a new game state is created.
-   *
-   * @return The loaded game state, or a new game state if the file does not exist.
-   */
-  public ChaosGame loadGameState() {
-    LOGGER.log(Level.INFO, "Loading game state.");
-    File file = new File(SERIALIZED_GAME_PATH);
-    if (file.exists()) {
-      try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
-        ChaosGame loadedGame = (ChaosGame) ois.readObject();
-        LOGGER.log(Level.INFO, "Game state loaded successfully.");
-        return loadedGame;
-      } catch (IOException | ClassNotFoundException e) {
-        LOGGER.log(Level.WARNING, "Failed to load game state. Creating new game.");
-      }
-    } else {
-      LOGGER.log(Level.WARNING, "No saved game state found. Creating new game.");
-    }
-    ChaosGame newGame = new ChaosGame(ChaosGameDescriptionFactory
-            .get(ChaosGameDescriptionFactory.DescriptionTypeEnum.SIERPINSKI_TRIANGLE),
-            650, 650);
-    newGame.setDescriptionName(ChaosGameDescriptionFactory.DescriptionTypeEnum
-            .SIERPINSKI_TRIANGLE.toString());
-    return newGame;
-  }
-
   /**
    * Adds a new custom fractal by creating a ChaosGameDescription based on the
    * parameters which is written to a file in the fractals' directory.
@@ -341,9 +212,9 @@ public class MainPageController {
     try {
       ChaosGameDescription newChaosGameDescription =
               new ChaosGameDescription(
-                      getVector2dFromStringList(minCoords),
-                      getVector2dFromStringList(maxCoords),
-                      getTransformListFromStringList(transformations)
+                      TransformationParser.getVector2dFromStringList(minCoords),
+                      TransformationParser.getVector2dFromStringList(maxCoords),
+                      TransformationParser.getTransformListFromStringList(transformations)
               );
       if (!Files.exists(Path.of(FRACTAL_PATH + fractalName + ".txt"))
               || view.askConfirmation("A custom fractal with the same name already exists. "
@@ -372,88 +243,6 @@ public class MainPageController {
     LOGGER.log(Level.INFO, "File saved successfully in {0}", file.getAbsolutePath());
   }
 
-  /**
-   * Creates a Vector2d object from a string array, containing the x and y coordinates.
-   *
-   * @param vector the string array containing the x and y coordinates
-   * @return the Vector2d object created from the string array
-   */
-  private Vector2d getVector2dFromStringList(String[] vector) {
-    try {
-      return new Vector2d(Double.parseDouble(vector[0]), Double.parseDouble(vector[1]));
-    } catch (NumberFormatException e) {
-      throw new IllegalArgumentException("Invalid coordinates. Please enter valid integers.");
-    }
-  }
-
-  /**
-   * Creates a list of Transform2D objects from a list of string arrays.
-   *
-   * @param transform the list of string arrays containing the transformation parameters
-   * @return the list of Transform2D objects created from the string arrays
-   */
-  private List<Transform2D> getTransformListFromStringList(List<String[]> transform)
-          throws NumberFormatException {
-    try {
-      List<Transform2D> transformList = new ArrayList<>();
-      for (String[] transformation : transform) {
-        if (transformation.length == 2) {
-          transformList.addAll(parseJuliaTransform(transformation));
-        } else if (transformation.length == 6) {
-          transformList.add(parseAffineTransform(transformation));
-        }
-      }
-      return transformList;
-    } catch (NumberFormatException e) {
-      throw new IllegalArgumentException("Invalid coordinates. Please "
-              + "enter valid decimal numbers.");
-    }
-
-  }
-
-  /**
-   * Parses the Julia transformations and returns a List of Julia Transformations.
-   *
-   * @param transformation the string array containing the transformation parameters
-   * @return the list of Julia Transformations
-   */
-  private List<Transform2D> parseJuliaTransform(String[] transformation) {
-    return List.of(
-            new JuliaTransform(
-                    new Complex(
-                            Double.parseDouble(transformation[0]),
-                            Double.parseDouble(transformation[1])
-                    ),
-                    1),
-            new JuliaTransform(
-                    new Complex(
-                            Double.parseDouble(transformation[0]),
-                            Double.parseDouble(transformation[1])
-                    ),
-                    1)
-    );
-  }
-
-  /**
-   * Parses the Affine transformations and returns a List of Julia Transformations.
-   *
-   * @param transformation the string array containing the transformation parameters
-   * @return the list of Affine Transformations
-   */
-  private AffineTransform2D parseAffineTransform(String[] transformation) {
-    return new AffineTransform2D(
-            new Matrix2x2(
-                    Double.parseDouble(transformation[0]),
-                    Double.parseDouble(transformation[1]),
-                    Double.parseDouble(transformation[2]),
-                    Double.parseDouble(transformation[3])
-            ),
-            new Vector2d(
-                    Double.parseDouble(transformation[4]),
-                    Double.parseDouble(transformation[5])
-            ));
-
-  }
 
   /**
    * Retrieves a list of all custom fractal files in the fractals directory.
diff --git a/src/main/java/edu/ntnu/idatt2003/launcher/CLILauncher.java b/src/main/java/edu/ntnu/idatt2003/launcher/CLILauncher.java
new file mode 100644
index 0000000000000000000000000000000000000000..40e804870da7f53574e2a02f7b8b0e05924a335f
--- /dev/null
+++ b/src/main/java/edu/ntnu/idatt2003/launcher/CLILauncher.java
@@ -0,0 +1,16 @@
+
+package edu.ntnu.idatt2003.launcher;
+
+import edu.ntnu.idatt2003.view.CommandLineInterface;
+
+/**
+ * Launcher for the command line interface
+ * To read or write from file write src/main/resources/fractals/filename(with .txt)
+ */
+
+public class CLILauncher {
+  public static void main(String[] args) {
+    CommandLineInterface menuDrivenCommandLine = new CommandLineInterface();
+    menuDrivenCommandLine.start();
+  }
+}
diff --git a/src/main/java/edu/ntnu/idatt2003/Main.java b/src/main/java/edu/ntnu/idatt2003/launcher/Main.java
similarity index 96%
rename from src/main/java/edu/ntnu/idatt2003/Main.java
rename to src/main/java/edu/ntnu/idatt2003/launcher/Main.java
index 20e09cc4070d5d3a4564cfa5ffb3f18b9a7d4df1..1c9edf412fa54b3091f02ff46feff204458413ab 100644
--- a/src/main/java/edu/ntnu/idatt2003/Main.java
+++ b/src/main/java/edu/ntnu/idatt2003/launcher/Main.java
@@ -1,4 +1,4 @@
-package edu.ntnu.idatt2003;
+package edu.ntnu.idatt2003.launcher;
 
 import edu.ntnu.idatt2003.controller.MainPageController;
 import javafx.application.Application;
diff --git a/src/main/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactory.java b/src/main/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactory.java
index 270e83644f9a44798ec97eaa32a1d55433701568..696e272d23921e9ae0195101e518733b6ad838c5 100644
--- a/src/main/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactory.java
+++ b/src/main/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactory.java
@@ -187,7 +187,7 @@ public class ChaosGameDescriptionFactory {
   public static ChaosGameDescription getCustom(String transformationName)
           throws FileNotFoundException{
     try {
-      String filePath = "src/main/resources/transformations/" + transformationName + ".txt";
+      String filePath = "src/main/resources/fractals/" + transformationName + ".txt";
       return readFractal(filePath);
     } catch (FileNotFoundException e) {
       throw new FileNotFoundException("File " + e.getMessage());
diff --git a/src/main/java/edu/ntnu/idatt2003/model/ChaosGameFileHandler.java b/src/main/java/edu/ntnu/idatt2003/model/ChaosGameFileHandler.java
index cbdc3813ae28eca2ef971b36f809aacfc1c15129..73db0d7dd6c18a37476d5e08363dbde8a0c35974 100644
--- a/src/main/java/edu/ntnu/idatt2003/model/ChaosGameFileHandler.java
+++ b/src/main/java/edu/ntnu/idatt2003/model/ChaosGameFileHandler.java
@@ -6,7 +6,9 @@ import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.InputMismatchException;
 import java.util.List;
@@ -22,6 +24,8 @@ import java.util.Scanner;
  */
 public class ChaosGameFileHandler {
 
+  public static final String FRACTAL_PATH = "src/main/resources/fractals/";
+
   /**
    * Private constructor to prevent instantiation.
    */
@@ -163,4 +167,32 @@ public class ChaosGameFileHandler {
     }
   }
 
+  /**
+   * Validates if the file exists.
+   *
+   * @param file the file to validate
+   *
+   * @return a boolean value
+   */
+
+  public static boolean validateFile(File file) {
+    try {
+      ChaosGameFileHandler.readFromFile(file);
+      return true;
+    } catch (InputMismatchException | FileNotFoundException e) {
+      return false;
+    }
+  }
+
+  /**
+   * Stores the file in the resources/transformations folder of the project.
+   *
+   * @param file The file to store.
+   */
+  public static void storeFile(File file) throws IOException {
+    String projectPath = System.getProperty("user.dir");
+    String destinationPath = projectPath + File.separator + FRACTAL_PATH + file.getName();
+    Files.copy(file.toPath(), Path.of(destinationPath), StandardCopyOption.REPLACE_EXISTING);
+  }
+
 }
diff --git a/src/main/java/edu/ntnu/idatt2003/utils/LoggerUtil.java b/src/main/java/edu/ntnu/idatt2003/utils/LoggerUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..10b2fae8f0e4ce8b599cb0268534a36518d716ef
--- /dev/null
+++ b/src/main/java/edu/ntnu/idatt2003/utils/LoggerUtil.java
@@ -0,0 +1,38 @@
+package edu.ntnu.idatt2003.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.SimpleFormatter;
+
+/**
+ * Utility class for setting up a Logger.
+ */
+public class LoggerUtil {
+
+  /**
+   * Sets up a logger for the specified class name.
+   *
+   * @param className the name of the class for which the logger is being set up
+   * @return the configured Logger instance
+   */
+  public static Logger setupLogger(String className) {
+    Logger logger = Logger.getLogger(className);
+    try {
+      File logDirectory = new File("logs");
+      if (!logDirectory.exists() && !logDirectory.mkdirs()) {
+        System.err.println("Failed to create log directory.");
+      }
+      FileHandler fileHandler = new FileHandler("logs/application.log", false);
+      fileHandler.setFormatter(new SimpleFormatter());
+      fileHandler.setLevel(Level.WARNING);
+      logger.addHandler(fileHandler);
+      logger.setLevel(Level.ALL);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return logger;
+  }
+}
diff --git a/src/main/java/edu/ntnu/idatt2003/utils/TransformationParser.java b/src/main/java/edu/ntnu/idatt2003/utils/TransformationParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..20944e7ebefd81e1922b137c1ed37e51004e122a
--- /dev/null
+++ b/src/main/java/edu/ntnu/idatt2003/utils/TransformationParser.java
@@ -0,0 +1,161 @@
+package edu.ntnu.idatt2003.utils;
+
+import edu.ntnu.idatt2003.model.AffineTransform2D;
+import edu.ntnu.idatt2003.model.ChaosGame;
+import edu.ntnu.idatt2003.model.Complex;
+import edu.ntnu.idatt2003.model.JuliaTransform;
+import edu.ntnu.idatt2003.model.Matrix2x2;
+import edu.ntnu.idatt2003.model.Transform2D;
+import edu.ntnu.idatt2003.model.Vector2d;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.DoubleStream;
+
+/**
+ * Utility class for handling transformations in the ChaosGame.
+ */
+public class TransformationParser {
+
+  /**
+   * private constructor to stop initialization
+   */
+  private TransformationParser() {
+  }
+
+  /**
+   * Returns the list of coordinate arrays of the game.
+   *
+   * @param game the ChaosGame instance
+   * @return the list of coordinate arrays of the game
+   */
+  public static List<double[]> getTransformList(ChaosGame game) {
+    if (fractalIsJulia(game)) {
+      return getTransformListJulia(game);
+    } else {
+      return getTransformListAffine(game);
+    }
+  }
+
+  /**
+   * Returns the list of coordinate arrays for a Julia fractal.
+   *
+   * @param game the ChaosGame instance
+   * @return the list of coordinate arrays for a Julia fractal
+   */
+  private static List<double[]> getTransformListJulia(ChaosGame game) {
+    List<double[]> transformList = new ArrayList<>();
+    transformList.add(((JuliaTransform) game.getTransformList().getFirst()).getPointAsList());
+    return transformList;
+  }
+
+  /**
+   * Returns the list of coordinate arrays for an affine fractal.
+   *
+   * @param game the ChaosGame instance
+   * @return the list of coordinate arrays for an affine fractal
+   */
+  private static List<double[]> getTransformListAffine(ChaosGame game) {
+    List<double[]> transformList = new ArrayList<>();
+    for (Transform2D transform : game.getTransformList()) {
+      transformList.add(DoubleStream.concat(DoubleStream.of(
+              ((AffineTransform2D) transform).getMatrixCoordsList()),
+          DoubleStream.of(((AffineTransform2D) transform).getVectorCoordsList())).toArray());
+    }
+    return transformList;
+  }
+
+  /**
+   * Checks if the current fractal is a Julia fractal.
+   *
+   * @param game the ChaosGame instance
+   * @return true if the fractal is a Julia fractal, false otherwise
+   */
+  public static boolean fractalIsJulia(ChaosGame game) {
+    try {
+      return game.getTransformList().getFirst() instanceof JuliaTransform;
+    } catch (IndexOutOfBoundsException e) {
+      return false;
+    }
+  }
+
+  /**
+   * Creates a Vector2d object from a string array containing the x and y coordinates.
+   *
+   * @param vector the string array containing the x and y coordinates
+   * @return the Vector2d object created from the string array
+   * @throws IllegalArgumentException if the coordinates are invalid
+   */
+  public static Vector2d getVector2dFromStringList(String[] vector) {
+    try {
+      return new Vector2d(Double.parseDouble(vector[0]), Double.parseDouble(vector[1]));
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException("Invalid coordinates. Please enter valid numbers.");
+    }
+  }
+
+  /**
+   * Creates a list of Transform2D objects from a list of string arrays.
+   *
+   * @param transform the list of string arrays containing the transformation parameters
+   * @return the list of Transform2D objects created from the string arrays
+   * @throws IllegalArgumentException if the coordinates are invalid
+   */
+  public static List<Transform2D> getTransformListFromStringList(List<String[]> transform) {
+    try {
+      List<Transform2D> transformList = new ArrayList<>();
+      for (String[] transformation : transform) {
+        if (transformation.length == 2) {
+          transformList.addAll(parseJuliaTransform(transformation));
+        } else if (transformation.length == 6) {
+          transformList.add(parseAffineTransform(transformation));
+        }
+      }
+      return transformList;
+    } catch (NumberFormatException e) {
+      throw new IllegalArgumentException("Invalid coordinates. Please enter valid numbers.");
+    }
+  }
+
+  /**
+   * Parses the Julia transformations and returns a list of JuliaTransform objects.
+   *
+   * @param transformation the string array containing the transformation parameters
+   * @return the list of JuliaTransform objects
+   */
+  private static List<Transform2D> parseJuliaTransform(String[] transformation) {
+    return List.of(
+        new JuliaTransform(
+            new Complex(
+                Double.parseDouble(transformation[0]),
+                Double.parseDouble(transformation[1])
+            ),
+            1),
+        new JuliaTransform(
+            new Complex(
+                Double.parseDouble(transformation[0]),
+                Double.parseDouble(transformation[1])
+            ),
+            1)
+    );
+  }
+
+  /**
+   * Parses the affine transformations and returns an AffineTransform2D object.
+   *
+   * @param transformation the string array containing the transformation parameters
+   * @return the AffineTransform2D object
+   */
+  private static AffineTransform2D parseAffineTransform(String[] transformation) {
+    return new AffineTransform2D(
+        new Matrix2x2(
+            Double.parseDouble(transformation[0]),
+            Double.parseDouble(transformation[1]),
+            Double.parseDouble(transformation[2]),
+            Double.parseDouble(transformation[3])
+        ),
+        new Vector2d(
+            Double.parseDouble(transformation[4]),
+            Double.parseDouble(transformation[5])
+        ));
+  }
+}
diff --git a/src/main/java/edu/ntnu/idatt2003/view/CommandLineInterface.java b/src/main/java/edu/ntnu/idatt2003/view/CommandLineInterface.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b73135a3d2581264b199551d9f401c76bbbaff5
--- /dev/null
+++ b/src/main/java/edu/ntnu/idatt2003/view/CommandLineInterface.java
@@ -0,0 +1,270 @@
+package edu.ntnu.idatt2003.view;
+
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.READ_FILE;
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.EXIT;
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.RUN_ITERATIONS;
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.SHOW_CANVAS;
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.WRITE_FILE;
+import static edu.ntnu.idatt2003.view.CommandLineMenuRenderer.WRITE_NEW_DESCRIPTION;
+
+import edu.ntnu.idatt2003.model.AffineTransform2D;
+import edu.ntnu.idatt2003.model.ChaosGame;
+import edu.ntnu.idatt2003.model.ChaosGameDescription;
+import edu.ntnu.idatt2003.model.ChaosGameFileHandler;
+import edu.ntnu.idatt2003.model.Complex;
+import edu.ntnu.idatt2003.model.JuliaTransform;
+import edu.ntnu.idatt2003.model.Matrix2x2;
+import edu.ntnu.idatt2003.model.Transform2D;
+import edu.ntnu.idatt2003.model.Vector2d;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * Represents a command-line interface for the ChaosGame application.
+ * This class handles user interactions through the console, allowing
+ * the user to read and write descriptions from/to files, run iterations,
+ * show the canvas, and create new descriptions.
+ */
+public class CommandLineInterface {
+  CommandLineMenuRenderer menuRenderer = new CommandLineMenuRenderer();
+  ChaosGameDescription description;
+  ChaosGame chaosGame;
+  private final Scanner scanner = new Scanner(System.in);
+
+  /**
+   * Starts the user interface. Asks the user for input and performs the corresponding action.
+   * Breaks the loop when the user chooses to exit, which is when the user inputs "6".
+   * Invalid input will result in a message.
+   */
+  public void start() {
+    String choice;
+    do {
+      menuRenderer.showMenu();
+      choice = getUserChoice();
+      switch (choice) {
+        case READ_FILE:
+          readDescriptionFromFile();
+          break;
+        case WRITE_FILE:
+          writeDescriptionToFile();
+          break;
+        case RUN_ITERATIONS:
+          runIterations();
+          break;
+        case SHOW_CANVAS:
+          showCanvas();
+          break;
+        case WRITE_NEW_DESCRIPTION:
+          createNewDescription();
+          break;
+        default:
+          System.out.println("Invalid choice");
+          break;
+      }
+    } while (!choice.equals(EXIT));
+  }
+
+  /**
+   * Gets the user's choice from the console.
+   *
+   * @return The user's choice as a String.
+   */
+  public static String getUserChoice() {
+    Scanner scanner = new Scanner(System.in);
+    return scanner.nextLine();
+  }
+
+  /**
+   * Gets the user's input from the console, ensuring it is not empty.
+   *
+   * @return The user's input as a String.
+   */
+  public String textInput() {
+    String text;
+    do {
+      text = scanner.nextLine();
+      if (text.isEmpty()) {
+        System.out.println("Input cannot be empty. Please try again.");
+      }
+    } while (text.isEmpty());
+    return text;
+  }
+
+  /**
+   * Gets the user's numeric input from the console, ensuring it is a valid number and not empty.
+   *
+   * @return The user's input as an int.
+   */
+  public int numberInput() {
+    int number;
+    while (true) {
+      String input = scanner.nextLine();
+      if (!input.isEmpty()) {
+        try {
+          number = Integer.parseInt(input);
+          break;
+        } catch (NumberFormatException e) {
+          System.out.println("Invalid input. Please enter a valid number.");
+        }
+      } else {
+        System.out.println("Input cannot be empty. Please try again.");
+      }
+    }
+    return number;
+  }
+
+  /**
+   * Gets the user's vector input from the console, ensuring it contains two valid numbers.
+   *
+   * @return The user's input as a Vector2d.
+   */
+  public Vector2d vectorInput() {
+    while (true) {
+      String input = scanner.nextLine();
+      if (!input.isEmpty()) {
+        String[] parts = input.split(" ");
+        if (parts.length == 2) {
+          try {
+            double x = Double.parseDouble(parts[0]);
+            double y = Double.parseDouble(parts[1]);
+            return new Vector2d(x, y);
+          } catch (NumberFormatException e) {
+            System.out.println("Invalid input. Please enter two valid numbers separated by a space.");
+          }
+        } else {
+          System.out.println("Invalid input. Please enter exactly two numbers separated by a space.");
+        }
+      } else {
+        System.out.println("Input cannot be empty. Please try again.");
+      }
+    }
+  }
+
+  public Matrix2x2 matrixInput() {
+    while (true) {
+      System.out.println("Enter matrix elements (a b c d):");
+      String input = scanner.nextLine();
+      if (!input.isEmpty()) {
+        String[] parts = input.split(" ");
+        if (parts.length == 4) {
+          try {
+            double a = Double.parseDouble(parts[0]);
+            double b = Double.parseDouble(parts[1]);
+            double c = Double.parseDouble(parts[2]);
+            double d = Double.parseDouble(parts[3]);
+            return new Matrix2x2(a, b, c, d);
+          } catch (NumberFormatException e) {
+            System.out.println("Invalid input. Please enter four valid numbers separated by spaces.");
+          }
+        } else {
+          System.out.println("Invalid input. Please enter exactly four numbers separated by spaces.");
+        }
+      } else {
+        System.out.println("Input cannot be empty. Please try again.");
+      }
+    }
+  }
+
+  /**
+   * Reads a ChaosGameDescription from a file specified by the user.
+   * Sets the description and initializes the chaosGame with the new description.
+   */
+  public void readDescriptionFromFile() {
+    menuRenderer.enterPath();
+    String pathToFile = textInput();
+    try {
+      ChaosGameDescription newDescription = ChaosGameFileHandler.readFromFile(pathToFile);
+      this.description = newDescription;
+      this.chaosGame = new ChaosGame(description, 30, 30);
+      System.out.println("File read and description set successfully");
+    } catch (FileNotFoundException e) {
+      System.out.println("File not found: " + pathToFile);
+    }
+  }
+
+  /**
+   * Writes the current ChaosGameDescription to a file specified by the user.
+   */
+  public void writeDescriptionToFile() {
+    menuRenderer.enterPath();
+    String pathToFile = textInput();
+    ChaosGameFileHandler.writeToFile(description, pathToFile);
+  }
+
+  /**
+   * Runs iterations for the chaos game. Asks the user for the number of steps to run the simulation.
+   */
+  public void runIterations() {
+    if (chaosGame == null || description == null) {
+      System.out.println("Description or chaosGame is null");
+      return;
+    }
+    menuRenderer.enterSteps();
+    int steps = numberInput();
+    chaosGame.runStepsAndUpdateTotal(steps);
+  }
+
+  /**
+   * Displays the canvas for the current chaos game.
+   */
+  public void showCanvas() {
+    if (chaosGame == null) {
+      System.out.println("ChaosGame is null");
+      return;
+    }
+    chaosGame.getCanvas().showCanvas();
+  }
+
+  /**
+   * Creates a new ChaosGameDescription based on user input.
+   */
+  public void createNewDescription() {
+    System.out.println("Enter minimum coordinates (x y):");
+    Vector2d minCoords = vectorInput();
+
+    System.out.println("Enter maximum coordinates (x y):");
+    Vector2d maxCoords = vectorInput();
+
+    List<Transform2D> transformations = new ArrayList<>();
+    System.out.println("Enter the number of transformations:");
+    int numTransformations = numberInput();
+
+    for (int i = 0; i < numTransformations; i++) {
+      System.out.println("Enter transformation type (affine/julia):");
+      String type = textInput();
+      if (type.equalsIgnoreCase("affine")) {
+        Matrix2x2 matrix = matrixInput();
+        System.out.println("Enter vector elements (e f):");
+        Vector2d vector = vectorInput();
+        transformations.add(new AffineTransform2D(matrix, vector));
+      } else if (type.equalsIgnoreCase("julia")) {
+        System.out.println("Enter real and imaginary parts of the complex number:");
+        double real = numberInput();
+        double imaginary = numberInput();
+        transformations.add(new JuliaTransform(new Complex(real, imaginary), 1));
+        transformations.add(new JuliaTransform(new Complex(real, imaginary), -1));
+      } else {
+        System.out.println("Invalid transformation type. Please enter 'affine' or 'julia'.");
+        i--;
+      }
+    }
+
+
+    ChaosGameDescription newDescription = new ChaosGameDescription(minCoords, maxCoords, transformations);
+    this.description = newDescription;
+    this.chaosGame = new ChaosGame(description, 30, 30);
+    System.out.println("New description created successfully");
+  }
+
+  /**
+   * The main method to run the command-line interface.
+   *
+   * @param args Command line arguments
+   */
+  public static void main(String[] args) {
+    CommandLineInterface cli = new CommandLineInterface();
+    cli.start();
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/ntnu/idatt2003/view/TextRenderer.java b/src/main/java/edu/ntnu/idatt2003/view/CommandLineMenuRenderer.java
similarity index 66%
rename from src/main/java/edu/ntnu/idatt2003/view/TextRenderer.java
rename to src/main/java/edu/ntnu/idatt2003/view/CommandLineMenuRenderer.java
index 9bc904284d8a84d088ab679c69bf45ec6d7b2f27..a864df131652f746b2781cc516b17cfd2fcf3ce3 100644
--- a/src/main/java/edu/ntnu/idatt2003/view/TextRenderer.java
+++ b/src/main/java/edu/ntnu/idatt2003/view/CommandLineMenuRenderer.java
@@ -7,31 +7,44 @@ package edu.ntnu.idatt2003.view;
  *
  */
 
-public class TextRenderer {
+public class CommandLineMenuRenderer {
 
   public static final String READ_FILE = "1";
   public static final String WRITE_FILE = "2";
   public static final String RUN_ITERATIONS = "3";
   public static final String SHOW_CANVAS = "4";
-  public static final String EXIT = "5";
+  public static final String WRITE_NEW_DESCRIPTION = "5";
+  public static final String EXIT = "6";
+
+  /**
+   * shows the menu
+   */
 
   public void showMenu() {
     System.out.println("Menu:");
     System.out.println("1. Read description from file");
-    System.out.println("2. Write description to file");
+    System.out.println("2. Write current description to file");
     System.out.println("3. Run ChaosGame a given number of steps");
     System.out.println("4. Show Canvas");
-    System.out.println("5. Exit");
+    System.out.println("5. Write New Description");
+    System.out.println("6. Exit");
   }
 
+  /**
+   * shows the enter path message
+   */
+
   public void enterPath() {
     System.out.println("Enter path to file:");
   }
 
+  /**
+   * shows the enter steps message
+   */
+
   public void enterSteps() {
     System.out.println("Enter number of steps:");
   }
 
 
-
 }
diff --git a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java
index 6a5e295e8e98b690c17f979a959fc81070f0c463..ce866efc9a09faf94d205d07f318b134ee9dc711 100644
--- a/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java
+++ b/src/main/java/edu/ntnu/idatt2003/view/MainPageView.java
@@ -7,6 +7,7 @@ import static edu.ntnu.idatt2003.view.components.TextBoxFactory.createTextBox;
 import static edu.ntnu.idatt2003.view.components.TextFieldFactory.createTextField;
 
 import edu.ntnu.idatt2003.controller.MainPageController;
+import edu.ntnu.idatt2003.utils.TransformationParser;
 import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory;
 import edu.ntnu.idatt2003.model.ChaosGameObserver;
 import edu.ntnu.idatt2003.utils.Sizes;
@@ -132,7 +133,7 @@ public class MainPageView extends Scene implements ChaosGameObserver {
     dynamicJuliaContainer.setAlignment(Pos.BOTTOM_CENTER);
     TextField x0Field = createTextField("x: ", 100, 20);
     TextField x1Field = createTextField("y: ", 100, 20);
-    if (controller.fractalIsJulia()) {
+    if (TransformationParser.fractalIsJulia(controller.getGame())) {
       VBox juliaInformationContainer = new VBox(DEFAULT_SPACING);
       HBox.setHgrow(juliaInformationContainer, Priority.ALWAYS);
       juliaInformationContainer.getChildren().addAll(
@@ -297,7 +298,7 @@ public class MainPageView extends Scene implements ChaosGameObserver {
   private TransformationType getTransformationComboBoxValue() {
     if (controller.isAddingCustomFractal()) {
       return selectedTransformation;
-    } else if (controller.fractalIsJulia()) {
+    } else if (TransformationParser.fractalIsJulia(controller.getGame())) {
       return TransformationType.JULIA;
     } else {
       return TransformationType.AFFINE;
@@ -332,7 +333,7 @@ public class MainPageView extends Scene implements ChaosGameObserver {
   private List<String[]> getJuliaTransformation(VBox transformationVbox) {
     List<String[]> list = new ArrayList<>();
     if (!transformationVbox.getChildren().isEmpty()) {
-      HBox juliaFields = (HBox) transformationVbox.getChildren().get(0);
+      HBox juliaFields = (HBox) transformationVbox.getChildren().getFirst();
       list.add(new String[]{((TextField) juliaFields.getChildren().get(1)).getText(),
               ((TextField) juliaFields.getChildren().get(2)).getText()});
     }
@@ -392,7 +393,7 @@ public class MainPageView extends Scene implements ChaosGameObserver {
       );
     }
     if (!controller.isAddingCustomFractal()) {
-      for (double[] coords : controller.getTransformList()) {
+      for (double[] coords : TransformationParser.getTransformList(controller.getGame())) {
         vbox.getChildren().add(createTextBoxWithTextFieldsContainer(DEFAULT_SPACING,
                 textBoxText, 55, 20, coords));
       }
diff --git a/src/main/java/edu/ntnu/idatt2003/view/UI.java b/src/main/java/edu/ntnu/idatt2003/view/UI.java
deleted file mode 100644
index 036465a0fb45affecfb85cd0ec7cee1cc7787969..0000000000000000000000000000000000000000
--- a/src/main/java/edu/ntnu/idatt2003/view/UI.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package edu.ntnu.idatt2003.view;
-
-import static edu.ntnu.idatt2003.view.TextRenderer.READ_FILE;
-import static edu.ntnu.idatt2003.view.TextRenderer.EXIT;
-import static edu.ntnu.idatt2003.view.TextRenderer.RUN_ITERATIONS;
-import static edu.ntnu.idatt2003.view.TextRenderer.SHOW_CANVAS;
-import static edu.ntnu.idatt2003.view.TextRenderer.WRITE_FILE;
-
-import edu.ntnu.idatt2003.model.ChaosGame;
-import edu.ntnu.idatt2003.model.ChaosGameDescription;
-import edu.ntnu.idatt2003.model.ChaosGameFileHandler;
-import java.io.FileNotFoundException;
-import java.util.Scanner;
-
-/**
- * Represents a user interface.
- * Contains methods for starting the UI, reading a description from a file, writing a description to a file,
- * running iterations and showing the canvas.
- * Includes a scanner for user input, and instances of TextRenderer, ChaosGameFileHandler, ChaosGame and ChaosGameDescription.
- * Goal: act as a view for a user interface.
- */
-
-public class UI {
-  TextRenderer textRenderer = new TextRenderer();
-  ChaosGameDescription description;
-  ChaosGame chaosGame;
-  private final Scanner scanner = new Scanner(System.in);
-
-  /**
-   * Starts the user interface. Asks the user for input and performs the corresponding action.
-   * Breaks the loop when the user chooses to exit, which is when the user inputs "5".
-   * Invalid input will result in a message.
-   */
-
-  public void start() {
-    String choice;
-    do {
-      textRenderer.showMenu();
-      choice = getUserChoice();
-      switch (choice) {
-        case READ_FILE:
-          readDescriptionFromFile();
-          break;
-        case WRITE_FILE:
-          writeDescriptionToFile();
-          break;
-        case RUN_ITERATIONS:
-          runIterations();
-          break;
-        case SHOW_CANVAS:
-          showCanvas();
-          break;
-        default:
-          System.out.println("Invalid choice");
-          break;
-      }
-    } while (!choice.equals(EXIT));
-  }
-
-  /**
-   * Gets the user's choice.
-   *
-   * @return The user's choice.
-   */
-
-  public static String getUserChoice() {
-    Scanner scanner = new Scanner(System.in);
-    return scanner.nextLine();
-  }
-
-  /**
-   * Gets the user's input, and checks if it is empty.
-   *
-   * @return The user's input.
-   */
-
-  public String textInput() {
-    String text;
-    do {
-      text = scanner.nextLine();
-      if (text.isEmpty()) {
-        System.out.println("Input cannot be empty. Please try again.");
-      }
-    } while (text.isEmpty());
-    return text;
-  }
-
-  /**
-   * Gets the user's input, and checks if it is a number and whether it is empty.
-   *
-   * @return The user's input.
-   */
-
-
-  public int numberInput() {
-    int number;
-    while (true) {
-      String input = scanner.nextLine();
-      if (!input.isEmpty()) {
-        try {
-          number = Integer.parseInt(input);
-          break;
-        } catch (NumberFormatException e) {
-          System.out.println("Invalid input. Please enter a valid number.");
-        }
-      } else {
-        System.out.println("Input cannot be empty. Please try again.");
-      }
-    }
-    return number;
-  }
-
-  /**
-   * Reads a description from a file.
-   * Asks the user for the path to the file, and reads the already existing description from the file.
-   * Sets the description and chaosGame to the new description.
-   * If the file is not found, a message will be shown.
-   */
-
-
-  public void readDescriptionFromFile(){
-    textRenderer.enterPath();
-    String pathToFile = textInput();
-    ChaosGameDescription newDescription;
-    try {
-      newDescription = ChaosGameFileHandler.readFromFile(pathToFile);
-      System.out.println("file read successfully");
-    } catch (FileNotFoundException e) {
-      throw new RuntimeException("File '" + pathToFile + "' not found." + e.getMessage());
-    }
-    this.description = newDescription;
-    chaosGame = new ChaosGame(description, 30, 30);
-    System.out.println("description set successfully");
-  }
-
-  /**
-   * Writes a description to a file.
-   * Asks the user for the path to the file, and writes the description to the file.
-   * If the file is not found, a message will be shown.
-   */
-
-  public void writeDescriptionToFile() {
-    textRenderer.enterPath();
-    String pathToFile = textInput();
-    ChaosGameFileHandler.writeToFile(description, pathToFile);
-  }
-
-  /**
-   * Runs iterations.
-   * Asks the user for the number of steps to run the simulation.
-   * Runs the simulation for the specified number of steps.
-   */
-
-  public void runIterations() {
-    if(chaosGame == null || description == null) {
-      System.out.println("description or chaosGame is null");
-      return;
-    }
-    textRenderer.enterSteps();
-    int steps = numberInput();
-    chaosGame.runStepsAndUpdateTotal(steps);
-  }
-
-  /**
-   * Shows the canvas, and ensures that chaosGame is not null.
-   */
-
-  public void showCanvas() {
-    if(chaosGame == null) {
-      System.out.println("chaosGame is null");
-      return;
-    }
-    chaosGame.getCanvas().showCanvas();
-  }
-
-}
diff --git a/src/test/java/edu/ntnu/idatt2003/controller/GameStateManagerTest.java b/src/test/java/edu/ntnu/idatt2003/controller/GameStateManagerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e12822483c33641460c12c310eb36e5f8612a22
--- /dev/null
+++ b/src/test/java/edu/ntnu/idatt2003/controller/GameStateManagerTest.java
@@ -0,0 +1,102 @@
+package edu.ntnu.idatt2003.controller;
+
+import edu.ntnu.idatt2003.model.ChaosGame;
+import edu.ntnu.idatt2003.model.ChaosGameDescriptionFactory;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.io.TempDir;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Test class for the GameStateManager class.
+ * This class contains tests for verifying the functionality of the GameStateManager.
+ */
+public class GameStateManagerTest {
+
+  @TempDir
+  Path tempDir;
+
+  private Path tempFilePath;
+  private ChaosGame game;
+
+  @BeforeEach
+  public void setUp() {
+    tempFilePath = tempDir.resolve("savedGameStateTest.ser");
+
+    game = new ChaosGame(
+        ChaosGameDescriptionFactory.get(
+            ChaosGameDescriptionFactory.DescriptionTypeEnum.SIERPINSKI_TRIANGLE),
+        650, 650);
+    game.setDescriptionName(
+        ChaosGameDescriptionFactory.DescriptionTypeEnum.SIERPINSKI_TRIANGLE.toString());
+  }
+
+  @Nested
+  @DisplayName("Positive Tests")
+  class positiveTest {
+
+    /**
+     * Tests that the game state is loaded correctly.
+     */
+    @Test
+    @DisplayName("Load game state")
+    public void testLoadGameState() {
+      GameStateManager.saveGameState(game);
+
+      ChaosGame loadedGame = GameStateManager.loadGameState();
+      assertNotNull(loadedGame, "Loaded game state should not be null");
+      assertEquals(game.getDescriptionName(), loadedGame.getDescriptionName(),
+          "Loaded game state should match saved state");
+    }
+
+    /**
+     * Tests that a new game state is created when no saved game state exists.
+     */
+    @Test
+    @DisplayName("Load new game state when no saved state exists")
+    public void testLoadNewGameStateWhenNoSavedStateExists() {
+      assertNotNull(game, "New game state should not be null");
+      assertEquals("SIERPINSKI_TRIANGLE", game.getDescriptionName(),
+          "New game state should have default description");
+    }
+
+
+  }
+
+  @Nested
+  @DisplayName("Negative tests")
+  class negativeTests {
+
+  /**
+   * Tests that an IOException is handled gracefully when saving the game state.
+   */
+  @Test
+  @DisplayName("Handle IOException during save")
+  public void testHandleIOExceptionDuringSave() {
+
+    assertDoesNotThrow(() -> GameStateManager.saveGameState(game),
+        "Saving game state should not throw an exception");
+  }
+
+  /**
+   * Tests that an IOException is handled gracefully when loading the game state.
+   */
+  @Test
+  @DisplayName("Handle IOException during load")
+  public void testHandleIOExceptionDuringLoad() {
+    try {
+      Files.write(tempFilePath, new byte[]{0, 1, 2, 3});
+    } catch (IOException e) {
+      fail("Unexpected exception writing to temp file: " + e.getMessage());
+    }
+
+    ChaosGame loadedGame = assertDoesNotThrow(() -> GameStateManager.loadGameState(),
+        "Loading game state should not throw an exception");
+    assertNotNull(loadedGame, "Loaded game state should not be null despite IOException");
+  }
+}
+
+}
diff --git a/src/test/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactoryTest.java b/src/test/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactoryTest.java
index 6be5ff0047ca1696ca9a45ce197629caff61b67c..2fb09763ab72296e9facc809cb598e247f049aa1 100644
--- a/src/test/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactoryTest.java
+++ b/src/test/java/edu/ntnu/idatt2003/model/ChaosGameDescriptionFactoryTest.java
@@ -23,6 +23,8 @@ public class ChaosGameDescriptionFactoryTest {
 
     /**
      * Tests getting the Sierpinski Triangle description.
+     * This test checks if the Sierpinski Triangle description is correctly created with the expected minimum and maximum coordinates
+     * and the correct number and types of transformations.
      */
     @Test
     @DisplayName("Get Sierpinski Triangle Test")
@@ -43,6 +45,8 @@ public class ChaosGameDescriptionFactoryTest {
 
     /**
      * Tests getting the Barnsley Fern description.
+     * This test checks if the Barnsley Fern description is correctly created with the expected minimum and maximum coordinates
+     * and the correct number and types of transformations.
      */
     @Test
     @DisplayName("Get Barnsley Fern Test")
@@ -64,6 +68,8 @@ public class ChaosGameDescriptionFactoryTest {
 
     /**
      * Tests getting the Julia transformation description.
+     * This test checks if the Julia transformation description is correctly created with the expected minimum and maximum coordinates
+     * and the correct number and types of transformations.
      */
     @Test
     @DisplayName("Get Julia Transformation Test")
@@ -80,6 +86,48 @@ public class ChaosGameDescriptionFactoryTest {
       assertInstanceOf(JuliaTransform.class, transforms.get(0));
       assertInstanceOf(JuliaTransform.class, transforms.get(1));
     }
+
+    /**
+     * Tests getting the Levy C Curve description.
+     * This test checks if the Levy C Curve description is correctly created with the expected minimum and maximum coordinates
+     * and the correct number and types of transformations.
+     */
+    @Test
+    @DisplayName("Get Levy C Curve Test")
+    void testGetLevyCCurve() {
+      ChaosGameDescription description = ChaosGameDescriptionFactory.get(ChaosGameDescriptionFactory.DescriptionTypeEnum.LEVY_C_CURVE);
+
+      assertNotNull(description);
+      assertEquals(new Vector2d(-1, -0.5).toString(), description.getMinCoords().toString());
+      assertEquals(new Vector2d(2, 1.5).toString(), description.getMaxCoords().toString());
+
+      List<Transform2D> transforms = description.getTransform();
+      assertEquals(2, transforms.size());
+
+      assertInstanceOf(AffineTransform2D.class, transforms.get(0));
+      assertInstanceOf(AffineTransform2D.class, transforms.get(1));
+    }
+
+    /**
+     * Tests getting the Dragon Curve description.
+     * This test checks if the Dragon Curve description is correctly created with the expected minimum and maximum coordinates
+     * and the correct number and types of transformations.
+     */
+    @Test
+    @DisplayName("Get Dragon Curve Test")
+    void testGetDragonCurve() {
+      ChaosGameDescription description = ChaosGameDescriptionFactory.get(ChaosGameDescriptionFactory.DescriptionTypeEnum.DRAGON_CURVE);
+
+      assertNotNull(description);
+      assertEquals(new Vector2d(-7, 0).toString(), description.getMinCoords().toString());
+      assertEquals(new Vector2d(6, 11).toString(), description.getMaxCoords().toString());
+
+      List<Transform2D> transforms = description.getTransform();
+      assertEquals(2, transforms.size());
+
+      assertInstanceOf(AffineTransform2D.class, transforms.get(0));
+      assertInstanceOf(AffineTransform2D.class, transforms.get(1));
+    }
   }
 
   /**
@@ -91,6 +139,8 @@ public class ChaosGameDescriptionFactoryTest {
 
     /**
      * Tests getting a custom transformation file that does not exist.
+     * This test checks if a FileNotFoundException is thrown when trying to get a custom transformation
+     * that does not exist.
      */
     @Test
     @DisplayName("Get Custom Transformation File Not Found Test")
@@ -99,7 +149,7 @@ public class ChaosGameDescriptionFactoryTest {
           ChaosGameDescriptionFactory.getCustom("non_existent_transformation")
       );
 
-      String expectedMessage = "File src/main/resources/transformations/non_existent_transformation.txt not found.";
+      String expectedMessage = "File src/main/resources/fractals/non_existent_transformation.txt not found.";
       String actualMessage = exception.getMessage();
 
       assertTrue(actualMessage.contains(expectedMessage));
@@ -110,4 +160,3 @@ public class ChaosGameDescriptionFactoryTest {
 
 
 
-
diff --git a/src/test/java/edu/ntnu/idatt2003/model/ChaosGameFileHandlerTest.java b/src/test/java/edu/ntnu/idatt2003/model/ChaosGameFileHandlerTest.java
index 7cf83cbc10af3d499a1c2630e94501c9c15f6807..b217ea5a5838fed2cf532dab0c8027bd65baf1fe 100644
--- a/src/test/java/edu/ntnu/idatt2003/model/ChaosGameFileHandlerTest.java
+++ b/src/test/java/edu/ntnu/idatt2003/model/ChaosGameFileHandlerTest.java
@@ -1,13 +1,13 @@
 package edu.ntnu.idatt2003.model;
 
 import org.junit.jupiter.api.*;
-
 import java.io.FileNotFoundException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.InputMismatchException;
 import java.util.List;
 
+
 import static org.junit.jupiter.api.Assertions.*;
 
 /**
@@ -126,36 +126,35 @@ class ChaosGameFileHandlerTest {
   class NegativeTests {
 
     /**
-     * Tests reading from a non-existing file.
-     * Verifies that a FileNotFoundException is thrown.
+     * Tests reading from a non-existing file. Verifies that a FileNotFoundException is thrown.
      */
     @Test
     @DisplayName("Test readChaosGameDescription with non-existing file")
     void testReadChaosGameDescriptionWithNonExistingFile() {
       assertThrows(FileNotFoundException.class, () -> ChaosGameFileHandler
-              .readFromFile("non-existing-file.txt"));
+          .readFromFile("non-existing-file.txt"));
     }
 
     /**
-     * Test reading from a file with an unknown transformation type.
-     * Verifies that an IllegalArgumentException is thrown.
+     * Test reading from a file with an unknown transformation type. Verifies that an
+     * IllegalArgumentException is thrown.
      */
     @Test
     @DisplayName("Test readChaosGameDescription with unknown transformation type")
     void testReadChaosGameDescriptionWithUnknownTransformationType() {
       assertThrows(IllegalArgumentException.class, () -> ChaosGameFileHandler
-              .readFromFile("src/test/resources/invalidNameExample.txt"));
+          .readFromFile("src/test/resources/invalidNameExample.txt"));
     }
 
     /**
-     * Tests reading from a file with an invalid number of arguments.
-     * Verifies that an IllegalArgumentException is thrown.
+     * Tests reading from a file with an invalid number of arguments. Verifies that an
+     * IllegalArgumentException is thrown.
      */
     @Test
     @DisplayName("Test readChaosGameDescription with invalid number of arguments")
     void testReadChaosGameDescriptionWithInvalidNumberOfArguments() {
       assertThrows(InputMismatchException.class, () -> ChaosGameFileHandler
-              .readFromFile("src/test/resources/invalidFormatExample.txt"));
+          .readFromFile("src/test/resources/invalidFormatExample.txt"));
     }
   }
 }
diff --git a/src/test/java/edu/ntnu/idatt2003/utils/LoggerUtilTest.java b/src/test/java/edu/ntnu/idatt2003/utils/LoggerUtilTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8081c04ea279091485befe73a7bb96d60a9ababe
--- /dev/null
+++ b/src/test/java/edu/ntnu/idatt2003/utils/LoggerUtilTest.java
@@ -0,0 +1,54 @@
+package edu.ntnu.idatt2003.utils;
+
+import org.junit.jupiter.api.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.File;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Test class for the LoggerUtil class.
+ * This class contains tests for verifying the configuration and functionality of the LoggerUtil.
+ */
+public class LoggerUtilTest {
+
+  private static final String LOG_FILE_PATH = "src/test/resources";
+  private static Logger logger;
+
+  /**
+   * Sets up the test environment before each test.
+   * Initializes the logger and ensures that the log file is deleted before each test.
+   */
+  @BeforeEach
+  public void setUp() {
+    logger = LoggerUtil.setupLogger(LoggerUtilTest.class.getName());
+  }
+
+  /**
+   * Tests that the logger is configured correctly.
+   * Verifies that the logger is not null, the log file is created, the logger has a FileHandler,
+   * and the logger level is set to ALL.
+   */
+  @Test
+  @DisplayName("Logger is configured correctly")
+  public void testLoggerConfiguration() {
+    assertNotNull(logger);
+
+    File logFile = new File(LOG_FILE_PATH);
+    assertTrue(logFile.exists(), "Log file should exist");
+
+    boolean hasFileHandler = false;
+    for (var handler : logger.getHandlers()) {
+      if (handler instanceof FileHandler) {
+        hasFileHandler = true;
+        break;
+      }
+    }
+    assertTrue(hasFileHandler, "Logger should have a FileHandler");
+
+    assertEquals(Level.ALL, logger.getLevel(), "Logger level should be ALL");
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idatt2003/utils/TransformationUtilTest.java b/src/test/java/edu/ntnu/idatt2003/utils/TransformationUtilTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ede8f67f92a7b4615a2546d2c3c200feb631c327
--- /dev/null
+++ b/src/test/java/edu/ntnu/idatt2003/utils/TransformationUtilTest.java
@@ -0,0 +1,153 @@
+package edu.ntnu.idatt2003.utils;
+
+import edu.ntnu.idatt2003.model.AffineTransform2D;
+import edu.ntnu.idatt2003.model.ChaosGame;
+import edu.ntnu.idatt2003.model.ChaosGameDescription;
+import edu.ntnu.idatt2003.model.Complex;
+import edu.ntnu.idatt2003.model.JuliaTransform;
+import edu.ntnu.idatt2003.model.Matrix2x2;
+import edu.ntnu.idatt2003.model.Transform2D;
+import edu.ntnu.idatt2003.model.Vector2d;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TransformationUtilTest {
+
+  private ChaosGame juliaGame;
+  private ChaosGame affineGame;
+  private List<String[]> juliaTransformStrings;
+  private List<String[]> affineTransformStrings;
+
+  @BeforeEach
+  public void setUp() {
+    List<Transform2D> juliaTransforms = new ArrayList<>();
+    juliaTransforms.add(new JuliaTransform(new Complex(0.355, 0.355), 1));
+    juliaGame = new ChaosGame(new ChaosGameDescription(
+        new Vector2d(0,0), new Vector2d(1,1),juliaTransforms), 800, 800);
+
+    List<Transform2D> affineTransforms = new ArrayList<>();
+    affineTransforms.add(new AffineTransform2D(
+        new Matrix2x2(0.5, 0, 0, 0.5), new Vector2d(1, 1)));
+    affineGame = new ChaosGame(new ChaosGameDescription(
+        new Vector2d(0,0), new Vector2d(1,1),affineTransforms), 800, 800);
+
+    juliaTransformStrings = new ArrayList<>();
+    juliaTransformStrings.add(new String[]{"0.355", "0.355"});
+
+    affineTransformStrings = new ArrayList<>();
+    affineTransformStrings.add(new String[]{"0.5", "0", "0", "0.5", "1", "1"});
+  }
+
+  @Nested
+  @DisplayName("Positive Tests")
+  class PositiveTests {
+
+    /**
+     * Tests that the transformation list for a Julia fractal is returned correctly.
+     */
+    @Test
+    @DisplayName("Get transformation list for Julia fractal")
+    public void testGetTransformListJulia() {
+      List<double[]> transformList = TransformationParser.getTransformList(juliaGame);
+      assertEquals(1, transformList.size());
+      assertArrayEquals(new double[]{0.355, 0.355}, transformList.getFirst());
+    }
+
+    /**
+     * Tests that the transformation list for an affine fractal is returned correctly.
+     */
+    @Test
+    @DisplayName("Get transformation list for affine fractal")
+    public void testGetTransformListAffine() {
+      List<double[]> transformList = TransformationParser.getTransformList(affineGame);
+      assertEquals(1, transformList.size());
+      assertArrayEquals(new double[]{0.5, 0, 0, 0.5, 1, 1}, transformList.getFirst());
+    }
+
+    /**
+     * Tests that the method correctly identifies a Julia fractal.
+     */
+    @Test
+    @DisplayName("Check if fractal is Julia")
+    public void testFractalIsJulia() {
+      assertTrue(TransformationParser.fractalIsJulia(juliaGame));
+    }
+
+    /**
+     * Tests that the method correctly identifies when the fractal is not a Julia fractal.
+     */
+    @Test
+    @DisplayName("Check if fractal is not Julia")
+    public void testFractalIsNotJulia() {
+      assertFalse(TransformationParser.fractalIsJulia(affineGame));
+    }
+
+    /**
+     * Tests the conversion from a string array to a Vector2d object.
+     */
+    @Test
+    @DisplayName("Convert string array to Vector2d")
+    public void testGetVector2dFromStringList() {
+      String[] vector = {"1.0", "2.0"};
+      Vector2d vec = TransformationParser.getVector2dFromStringList(vector);
+      assertEquals(1.0, vec.getX0());
+      assertEquals(2.0, vec.getX1());
+    }
+
+    /**
+     * Tests the creation of JuliaTransform objects from string arrays.
+     */
+    @Test
+    @DisplayName("Create JuliaTransform list from string arrays")
+    public void testGetTransformListFromStringListJulia() {
+      List<Transform2D> transforms = TransformationParser.getTransformListFromStringList(juliaTransformStrings);
+      assertEquals(2, transforms.size());
+      assertTrue(transforms.get(0) instanceof JuliaTransform);
+      assertTrue(transforms.get(1) instanceof JuliaTransform);
+    }
+
+    /**
+     * Tests the creation of AffineTransform2D objects from string arrays.
+     */
+    @Test
+    @DisplayName("Create AffineTransform2D list from string arrays")
+    public void testGetTransformListFromStringListAffine() {
+      List<Transform2D> transforms = TransformationParser.getTransformListFromStringList(affineTransformStrings);
+      assertEquals(1, transforms.size());
+      assertTrue(transforms.getFirst() instanceof AffineTransform2D);
+    }
+  }
+
+  @Nested
+  @DisplayName("Negative Tests")
+  class NegativeTests {
+
+    /**
+     * Tests that an IllegalArgumentException is thrown for invalid input when converting string array to Vector2d.
+     */
+    @Test
+    @DisplayName("Invalid input for converting string array to Vector2d")
+    public void testGetVector2dFromStringListInvalid() {
+      String[] vector = {"abc", "2.0"};
+      assertThrows(IllegalArgumentException.class, () -> TransformationParser.getVector2dFromStringList(vector));
+    }
+
+    /**
+     * Tests that an IllegalArgumentException is thrown for invalid input when creating Transform2D list from string arrays.
+     */
+    @Test
+    @DisplayName("Invalid input for creating Transform2D list from string arrays")
+    public void testGetTransformListFromStringListInvalid() {
+      List<String[]> transformStrings = new ArrayList<>();
+      transformStrings.add(new String[]{"abc", "0", "0", "0.5", "1", "1"});
+      assertThrows(IllegalArgumentException.class, () -> TransformationParser.getTransformListFromStringList(transformStrings));
+    }
+  }
+}