diff --git a/Assets/Scripts/DataPersistence/Data/GameData.cs b/Assets/Scripts/DataPersistence/Data/GameData.cs
index 3efdecc5d35ca3653eac2a8f826bcd506c6cfe9e..16804de39d1636629aca27e8619839518e02020c 100644
--- a/Assets/Scripts/DataPersistence/Data/GameData.cs
+++ b/Assets/Scripts/DataPersistence/Data/GameData.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
@@ -5,6 +6,9 @@ using UnityEngine;
 [System.Serializable]
 public class GameData
 {
+    public string saveName;
+    public DateTime currentTimeUtc;
+
     public int currentLevel;
     public Vector3 playerPosition;
 
@@ -27,8 +31,6 @@ public class GameData
     public List<PetData> pet = new List<PetData>();
 
 
-    // the values defined in this constructor will be the default values
-    // the game starts with when there's no data to load
     public GameData()
     {
 
@@ -42,4 +44,9 @@ public class PetData
     public int currentHealth;
     public int damagePerShot;
     public float speed;
+
+    public PetData()
+    {
+
+    }
 }
\ No newline at end of file
diff --git a/Assets/Scripts/DataPersistence/DataPersistenceManager.cs b/Assets/Scripts/DataPersistence/DataPersistenceManager.cs
index 488d07c0bf30aa5384326e0eddfdeb31e1562661..6cc908cb4a89e0a256a0d72b876c0d9559f20b33 100644
--- a/Assets/Scripts/DataPersistence/DataPersistenceManager.cs
+++ b/Assets/Scripts/DataPersistence/DataPersistenceManager.cs
@@ -12,6 +12,8 @@ public class DataPersistenceManager : MonoBehaviour
     private List<IDataPersistence> dataPersistenceObjects;
     private FileDataHandler dataHandler;
 
+    private string selectedProfileId = "test";
+
     public static DataPersistenceManager instance { get; private set; }
 
     private void Awake()
@@ -38,7 +40,7 @@ public class DataPersistenceManager : MonoBehaviour
     public void LoadGame()
     {
         // load any saved data from a file using data handler
-        this.gameData = dataHandler.Load();
+        this.gameData = dataHandler.Load(selectedProfileId);
 
         // if no data can be loaded, initialize to a new game
         if (this.gameData == null)
@@ -63,7 +65,7 @@ public class DataPersistenceManager : MonoBehaviour
         Debug.Log("Saved game data");
 
         // save that data to a file using data handler
-        dataHandler.Save(gameData);
+        dataHandler.Save(gameData, selectedProfileId);
     }
 
     //private void OnApplicationQuit()
@@ -76,4 +78,14 @@ public class DataPersistenceManager : MonoBehaviour
         IEnumerable<IDataPersistence> dataPersistenceObjects = FindObjectsOfType<MonoBehaviour>().OfType<IDataPersistence>();
         return new List<IDataPersistence>(dataPersistenceObjects);
     }
+
+    public bool HasGameData()
+    {
+        return gameData != null;
+    }
+
+    public Dictionary<string, GameData> GetAllProfilesGameData()
+    {
+        return dataHandler.LoadAllProfiles();
+    }
 }
diff --git a/Assets/Scripts/DataPersistence/FileDataHandler.cs b/Assets/Scripts/DataPersistence/FileDataHandler.cs
index efc010b887a58962072feeb54c22b872e38f2ac8..dcc2d88544b1014e469d1c31227a9bc5cee7d20e 100644
--- a/Assets/Scripts/DataPersistence/FileDataHandler.cs
+++ b/Assets/Scripts/DataPersistence/FileDataHandler.cs
@@ -15,10 +15,10 @@ public class FileDataHandler
         this.dataFileName = dataFileName;
     }
 
-    public GameData Load()
+    public GameData Load(string profileId)
     {
         // use Path.combine to account for different OS's having different path separators
-        string fullPath = Path.Combine(dataDirPath, dataFileName);
+        string fullPath = Path.Combine(dataDirPath, profileId, dataFileName);
         GameData loadedData = null;
         if (File.Exists(fullPath))
         {
@@ -45,10 +45,10 @@ public class FileDataHandler
         return loadedData;
     }
 
-    public void Save(GameData data)
+    public void Save(GameData data, string profileId)
     {
         // use Path.combine to account for different OS's having different path separators
-        string fullPath = Path.Combine(dataDirPath, dataFileName);
+        string fullPath = Path.Combine(dataDirPath, profileId, dataFileName);
         try
         {
             // create the directory the file will be written to if it doesn't already exist
@@ -71,4 +71,40 @@ public class FileDataHandler
             Debug.LogError("Error occured when trying to save data to file: " + fullPath + "\n" + e);
         }
     }
+
+    public Dictionary<string, GameData> LoadAllProfiles()
+    {
+        Dictionary<string, GameData> profileDictionary = new Dictionary<string, GameData>();
+
+        // loop over all directory names in the data directory path
+        IEnumerable<DirectoryInfo> dirInfos = new DirectoryInfo(dataDirPath).EnumerateDirectories();
+        foreach (DirectoryInfo dirInfo in dirInfos)
+        {
+            string profileId = dirInfo.Name;
+
+            // defensife programming - check if the data file exists
+            // if it doesn't, then this folder isn't a profile and should be skipped
+            string fullPath = Path.Combine(dataDirPath, profileId, dataFileName);
+            if (!File.Exists(fullPath))
+            {
+                Debug.LogWarning("Skipping directory when loading all profiles because it doesn't contain data: " + profileId);
+                continue;
+            }
+
+            // load the fame data for this profile and put it in the dictionary
+            GameData profileData = Load(profileId);
+
+            // defensive programming - ensure the profile data isn't null
+            // because if it is then something went wrong and we should let ourselves know
+            if (profileData != null)
+            {
+                profileDictionary.Add(profileId, profileData);
+            } else
+            {
+                Debug.LogError("Tried to load profile but something went wrong. ProfileId: " + profileId);
+            }
+        }
+
+        return profileDictionary;
+    }
 }
diff --git a/Assets/Scripts/Save.meta b/Assets/Scripts/Save.meta
new file mode 100644
index 0000000000000000000000000000000000000000..42ecb6a360aed0e890cf48a3983b84e777d30e02
--- /dev/null
+++ b/Assets/Scripts/Save.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0307f5f70bbd57245b5756a9fd0b65d1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/Save/SaveSlot.cs b/Assets/Scripts/Save/SaveSlot.cs
new file mode 100644
index 0000000000000000000000000000000000000000..58c3ff1d3c9bfcf3ca526a81f95a75edd83356f3
--- /dev/null
+++ b/Assets/Scripts/Save/SaveSlot.cs
@@ -0,0 +1,39 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using TMPro;
+
+public class SaveSlot : MonoBehaviour
+{
+    [Header("Profile")]
+    [SerializeField] private string profileId = "";
+
+    [Header("Content")]
+
+    [SerializeField] private GameObject noDataContent;
+    [SerializeField] private GameObject hasDataContent;
+    [SerializeField] private TextMeshProUGUI saveNameText;
+    [SerializeField] private TextMeshProUGUI timestampText;
+
+    public void SetData (GameData data)
+    {
+        // there's no data for this profileId
+        if (data == null)
+        {
+            noDataContent.SetActive(true);
+            hasDataContent.SetActive(false);
+        }
+        // there is data for this profileId
+        else
+        {
+            noDataContent.SetActive(false);
+            hasDataContent.SetActive(true);
+        }
+    }
+
+    public string GetProfileId()
+    {
+        return this.profileId;
+    }
+
+}
diff --git a/Assets/Scripts/Save/SaveSlot.cs.meta b/Assets/Scripts/Save/SaveSlot.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..882e8055e1c2d272a9473ccfc4d2c51c5a8293e8
--- /dev/null
+++ b/Assets/Scripts/Save/SaveSlot.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2173f7f1b7811c040be039d8cb6be761
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/Save/SaveSlotsMenu.cs b/Assets/Scripts/Save/SaveSlotsMenu.cs
new file mode 100644
index 0000000000000000000000000000000000000000..01742e247462b320733507001c84d6ae43c2361a
--- /dev/null
+++ b/Assets/Scripts/Save/SaveSlotsMenu.cs
@@ -0,0 +1,27 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SaveSlotsMenu : MonoBehaviour
+{
+    private SaveSlot[] saveSlots;
+
+    private void Awake()
+    {
+        saveSlots = this.GetComponentsInChildren<SaveSlot>();
+    }
+
+    public void ActivateMenu()
+    {
+        // load all of the profiles that exist
+        Dictionary<string, GameData> profilesGameData = DataPersistenceManager.instance.GetAllProfilesGameData();
+
+        // loop through each save slot in the UI and set the content appropriately
+        foreach (SaveSlot saveSlot in saveSlots)
+        {
+            GameData profileData = null;
+            profilesGameData.TryGetValue(saveSlot.GetProfileId(), out profileData);
+            saveSlot.SetData(profileData);
+        }
+    }
+}
diff --git a/Assets/Scripts/Save/SaveSlotsMenu.cs.meta b/Assets/Scripts/Save/SaveSlotsMenu.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..07ca03b7d6297bb228266f43647a59cf2c8b9196
--- /dev/null
+++ b/Assets/Scripts/Save/SaveSlotsMenu.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0175ea50dd35de04dae7a0bff1648fbf
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: