diff --git a/Manifest.toml b/Manifest.toml
index 75d18cc04331ae3013e00c4610336700ae88b1a2..7706479ff4fb185d0d95e71ec6333aa3d3b85684 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -2,7 +2,7 @@
 
 julia_version = "1.9.3"
 manifest_format = "2.0"
-project_hash = "9170528eea0c39d465f2660dd18c961dc469bd3c"
+project_hash = "75d54abf5eeb3f6918bd64a71e5b724e43cc9f03"
 
 [[deps.AbstractFFTs]]
 deps = ["LinearAlgebra"]
@@ -1411,11 +1411,11 @@ version = "2.7.2"
 
 [[deps.Persefone]]
 deps = ["Agents", "ArgParse", "CSV", "CairoMakie", "DataFrames", "DataFramesMeta", "Dates", "Distributed", "FileIO", "GeoArrays", "ImageMagick", "Logging", "LoggingExtras", "Pkg", "Random", "Serialization", "StableRNGs", "StatsBase", "TOML", "Test", "TiffImages"]
-git-tree-sha1 = "389b532a6c7343ce8a8c5842a7949f98330b4d49"
+git-tree-sha1 = "88ace44808fa1050135c13c31baec6c94ba14f38"
 repo-rev = "master"
 repo-url = "../model"
 uuid = "039acd1d-2a07-4b33-b082-83a1ff0fd136"
-version = "0.3.2"
+version = "0.3.5"
 
 [[deps.Pixman_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"]
diff --git a/Project.toml b/Project.toml
index e91dfd7dca058ddbd51bb737cfb6916b68f3d61e..3ba035d7acd8d51d58d3e4a0fd4eb352be8450d6 100644
--- a/Project.toml
+++ b/Project.toml
@@ -5,6 +5,7 @@ version = "0.1.0"
 
 [deps]
 Agents = "46ada45e-f475-11e8-01d0-f70cc89e6671"
+CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
 CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
 DataFramesMeta = "1313f7d8-7da2-5740-9ea0-a2ca25f37964"
 Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
diff --git a/src/GUI.jl b/src/GUI.jl
index 25aea7c645546f10219b644834dde59c5523295f..9f03a229cbffc92f773c603ce1fe09e73d81ba1e 100644
--- a/src/GUI.jl
+++ b/src/GUI.jl
@@ -5,7 +5,7 @@
 
 
 function loadsimulation(filename)
-    global model
+    global model, landcovermap
     @emit showSplash()
     fn = convert(filename, String)
     startswith(fn, "file://") && (fn = fn[8:end])
@@ -36,13 +36,23 @@ function Base.convert(s::QString, ::Type{<:AbstractString})
     s2
 end
 
-#TODO saveoutput()
+function saveoutput()
+    global model
+    println("Saving output to file")
+    if !@param(core.csvoutput) #override preference
+        for output in model.dataoutputs
+            CSV.write(joinpath(@param(core.outdir), output.name*".csv"),
+                      model.datatables[output.name])
+        end
+    end
+    visualiseoutput(model)
+    GLMakie.activate!() # visualiseoutput() switches to Cairo
+end
  
 function render_map(screen)
     global model, mapimage, landcovermap
     launching[] && return display(screen, Figure().scene) # blank screen at launch
     println("Updating map")
-    #FIXME I'm not sure date-1 is the real fix
     #FIXME repeatedly calling `visualisemap` causes the memory leak
     # (regardless of whether individuals are plotted or just the map)
     #mapimage[] = visualisemap(model, date[]-Day(1), landcovermap)
@@ -54,7 +64,7 @@ end
 function vismap(model::AgentBasedModel,date=nothing,landcover=nothing)
     # load and plot the map
     # Note: if the landcover map is supplied, it needs to be rotr90'ed
-    isnothing(landcover) && (landcover = load(@param(world.landcovermap)))
+    isnothing(landcover) && (landcover = rotr90(load(@param(world.landcovermap))))
     f = Figure()
     ax = Axis(f[1,1])
     hidedecorations!(ax)
@@ -79,7 +89,7 @@ function vismap(model::AgentBasedModel,date=nothing,landcover=nothing)
 end
 
 function render_plot(screen)
-    #FIXME causes segfault when called during a simulation?
+    #FIXME Y axis is too small -> lines not shown?
     global model
     launching[] && return display(screen, Figure().scene) # blank screen at launch
     println("Updating plot")
@@ -121,6 +131,7 @@ function activateqmlfunctions()
     @qmlfunction previousstep
     @qmlfunction datestring
     @qmlfunction writeconfig
+    @qmlfunction saveoutput
 end
 
 """
diff --git a/src/PersefoneDesktop.jl b/src/PersefoneDesktop.jl
index 75d8489deb5a027d78aa244c85aa25c8fda9fe97..66ed3d04b17f4589d343b00f0feb726fbad774a5 100644
--- a/src/PersefoneDesktop.jl
+++ b/src/PersefoneDesktop.jl
@@ -13,6 +13,7 @@
 module PersefoneDesktop
 
 using
+    CSV,
     QML,
     TOML,
     Dates,
diff --git a/src/logic.jl b/src/logic.jl
index ddf660780fda8779c6e64745de909bcb9f4341e2..3e9ef548d49bd250850af930a1875ac1d78e01ed 100644
--- a/src/logic.jl
+++ b/src/logic.jl
@@ -5,16 +5,17 @@
 
 @resumable function createlaunchfunction()
     global model, landcovermap
-    Ps = Persefone
     running[] = false
     progress[] = 0.0
     @yield 1
     model = initialise(configfile[])
     @yield 1
+    model.date -= Day(1)
+    Persefone.outputdata(model, true) # record data before run starts
+    model.date += Day(1)
     landcovermap = rotr90(load(@param(world.landcovermap)))
     date[] = model.date
     launching[] = false
-    println("Model initialised (resumable).")
 end
 
 function nextstep()
diff --git a/src/main.qml b/src/main.qml
index 28be96994a3e630496cc1a9196e6a958eb7d10ce..06f179e17d6eced9a6ba83dee0b9be69530cac32 100644
--- a/src/main.qml
+++ b/src/main.qml
@@ -30,15 +30,11 @@ ApplicationWindow {
 			}
 			Action {
 				text: "&Load Saved State"
-				onTriggered: {
-					loadFileChooser.open()
-				}
+				onTriggered: { loadFileChooser.open() }
 			}
 			Action {
 				text: "&Save Current State"
-				onTriggered: {
-					saveFileChooser.open()
-				}
+				onTriggered: { saveFileChooser.open() }
 			}
 			MenuSeparator { }
 			Action {
@@ -52,8 +48,10 @@ ApplicationWindow {
 				text: "Show &Population Graph"
 				onTriggered: { populationGraph.visible = true }
 			}
-			Action { text: "Save &Graphical Output" }
-			Action { text: "Save &CSV Output" }
+			Action {
+				text: "Save &Simulation Output"
+				onTriggered: { Julia.saveoutput() }
+			}
 		}
 		Menu {
 			title: "&Help"