diff --git a/Manifest.toml b/Manifest.toml
index be1ca3fafc7e2a39a580d9000435a12e4a9136bc..4a51b1a671cd9e3bce5f2ddd7a1534935d09d2c4 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -1410,12 +1410,12 @@ uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
 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"]
-git-tree-sha1 = "b24521422c9b1a1099a8e9d1c4db06c4fdd7050a"
-repo-rev = "development"
+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"
+repo-rev = "master"
 repo-url = "../model"
 uuid = "039acd1d-2a07-4b33-b082-83a1ff0fd136"
-version = "0.2.0"
+version = "0.3.2"
 
 [[deps.Pixman_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"]
@@ -1487,11 +1487,11 @@ version = "1.9.0"
 
 [[deps.QML]]
 deps = ["ColorTypes", "CxxWrap", "Libdl", "MacroTools", "Observables", "Qt6Wayland_jll", "Requires", "jlqml_jll"]
-git-tree-sha1 = "676fd7e228e5895c3a45e3d5fea3a619133c3af8"
+git-tree-sha1 = "04a0b01b4bb6aff4f16be3854e019bd750dc5a3e"
 repo-rev = "main"
 repo-url = "https://github.com/JuliaGraphics/QML.jl.git"
 uuid = "2db162a6-7e43-52c3-8d84-290c1c42d82a"
-version = "0.8.0"
+version = "0.8.1"
 
 [[deps.QOI]]
 deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
diff --git a/guiparams.toml b/guiparams.toml
new file mode 100644
index 0000000000000000000000000000000000000000..04d95ec40fc90b423ad544c9adddbaa49310dfdd
--- /dev/null
+++ b/guiparams.toml
@@ -0,0 +1,41 @@
+### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe.
+###
+### This is the default configuration file for Persefone, containing all model parameters.
+### The syntax is described here: https://toml.io/en/
+###
+### DO NOT MODIFY THIS FILE FOR SIMULATION EXPERIMENTS!
+### Instead, copy it to another directory and modify the copy.
+
+[core]
+configfile = "guiparams.toml" # location of the configuration file
+outdir = "desktop_results" # location and name of the output folder
+overwrite = true # overwrite the output directory? (true/false/"ask")
+csvoutput = false # save collected data in CSV files
+visualise = false # generate result graphs
+storedata = true # keep collected data in memory
+loglevel = "debug" # verbosity level: "debug", "info", "warn"
+processors = 2 # number of processors to use on parallel runs
+seed = 2 # seed value for the RNG (0 -> random value)
+# dates to start and end the simulation
+startdate = 2022-01-01
+enddate = 2022-12-31
+
+[world]
+landcovermap = "data/landcover_jena.tif" # location of the landcover map
+farmfieldsmap = "data/fields_jena.tif" # location of the field geometry map
+weatherfile = "data/weather_jena.csv" # location of the weather data file
+	
+[farm]
+farmmodel = "FieldManager" # which version of the farm model to use (not yet implemented)
+
+[nature]
+targetspecies = ["Wolpertinger", "Wyvern"] # list of target species to simulate
+popoutfreq = "daily" # output frequency population-level data, daily/monthly/yearly/end/never
+indoutfreq = "daily" # output frequency individual-level data, daily/monthly/yearly/end/never
+insectmodel = ["season", "habitat", "pesticides", "weather"] # factors affecting insect growth
+	
+[crop]
+cropmodel = "almass" # crop growth model to use, "almass" or "aquacrop"
+cropfile = "data/crop_data_general.csv" # file with general crop parameters
+growthfile = "data/almass_crop_growth_curves.csv" # file with crop growth parameters	
+
diff --git a/run.jl b/run.jl
index 09d304a16fdf4cd1549b5a974538959de3526a5c..fd96f76b97c043b6bad596932cc2c31370cebf8f 100755
--- a/run.jl
+++ b/run.jl
@@ -7,7 +7,4 @@ using Pkg
 Pkg.activate(".")
 using PersefoneDesktop
 
-# MUST disable threading in Qt
-ENV["QSG_RENDER_LOOP"] = "basic"
-
 launch()
diff --git a/src/GUI.jl b/src/GUI.jl
index d0f4c6e790aa5940ea301ab89d6fcf671bc04b95..271edf47fe5580638fabdb83f57959fdce4adafd 100644
--- a/src/GUI.jl
+++ b/src/GUI.jl
@@ -8,13 +8,15 @@
 ## - https://github.com/barche/QmlJuliaExamples
 ## - https://doc.qt.io/qt-6/qtquick-index.html
 
-#TODO define standard config file for Persefone Desktop
+#FIXME we either have a memory leak, and/or too much memory consumed
+# -> leads to SIGKILL after 3 months
 
 global model = nothing
 global landcovermap = nothing
 global runsimulation = nothing
 global timer = nothing
 
+const configfile = Observable("guiparams.toml")
 const running = Observable(false)
 const ticks = Observable(0)
 const date = Observable(today())
@@ -27,7 +29,7 @@ function newsimulation()
     global model, landcovermap
     running[] = false
     progress[] = 0.0
-    model = initialise() #TODO configure
+    model = initialise(configfile[])
     landcovermap = load(@param(world.landcovermap))
     date[] = model.date
     println("Model initialised.")
@@ -72,11 +74,19 @@ function previousstep()
     println("Backtracked model: $(date[]-Day(1))")
 end
 
+#XXX This could also be done with Channels rather than ResumableFunctions,
+# which would save us one dependency (Channels are inbuilt). However, the
+# ResumableFunctions version seemed to work more smoothly.
 @resumable function createrunfunction()
+    i = round(delay[]*10)
     while running[]
-        nextstep()
-        #XXX to reduce interface lag, always only sleep for 0.1s?
-        sleep(delay[])
+        i -= 1
+        if i <= 0
+            nextstep()
+            i = round(delay[]*10)
+        end
+        # to reduce interface lag, always only sleep for 0.1s
+        sleep(0.1)
         @yield 1
     end
 end
@@ -98,13 +108,15 @@ function togglerunning()
 end
 
 function render_map(screen)
+    #FIXME individuals not plotted
     global model, landcovermap
     println("Updating map")
-    figure = visualisemap(model, date[], landcovermap)
+    figure = visualisemap(model, date[]-Day(1), landcovermap)
     display(screen, figure.scene)
 end
 
 function render_plot(screen)
+    #FIXME causes segfault when called during a simulation
     global model
     println("Updating plot")
     figure = populationtrends(model)
@@ -124,6 +136,8 @@ on(ticks) do t
     running[] && runsimulation()
 end
 
+datestring = () -> Dates.format(date[], "dd U yyyy")
+
 function splashscreen()
     qmlfile = joinpath(dirname(@__FILE__), "splash.qml")
     quick_view = init_qquickview()
@@ -131,7 +145,7 @@ function splashscreen()
     QML.show(quick_view)
     #XXX exec_async() is currently still broken, but should be fixed soon
     # (https://github.com/JuliaGraphics/QML.jl/issues/174)
-    @async exec()
+    @async exec() #FIXME do this with the timer?
 end
 
 """
@@ -142,15 +156,19 @@ The main function that creates the application.
 function launch()
     global model, timer
     #splashscreen()
-    newsimulation()
+    isnothing(model) && newsimulation()
     
     @qmlfunction newsimulation
     @qmlfunction nextstep
     @qmlfunction previousstep
     @qmlfunction togglerunning
+    @qmlfunction datestring
 
     timer = QTimer()
 
+    ENV["QSG_RENDER_LOOP"] = "basic"
+    QML.setGraphicsApi(QML.OpenGL)
+    
     qmlfile = joinpath(dirname(@__FILE__), "main.qml")
     loadqml(qmlfile,
             timer = timer,
diff --git a/src/PersefoneDesktop.jl b/src/PersefoneDesktop.jl
index 4d9372911c2e14a3483db4cb85fd8566a41fa7d9..72ab48235775c2a972905de24b4ee972448409fd 100644
--- a/src/PersefoneDesktop.jl
+++ b/src/PersefoneDesktop.jl
@@ -25,11 +25,11 @@ using
     Observables,
     ResumableFunctions
 
-# needed for Makie integration
-ENV["QSG_RENDER_LOOP"] = "basic"
-
 include("GUI.jl")
 
+precompile(launch, ())
+#precompile(render_map, (Screen)) #what's the input type?
+
 export
     launch
 
diff --git a/src/config.qml b/src/config.qml
deleted file mode 100644
index 0a1e50c2d0e109b9321a16e5b459233c032489e3..0000000000000000000000000000000000000000
--- a/src/config.qml
+++ /dev/null
@@ -1,9 +0,0 @@
-/// PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
-///
-/// This file defines the layout of the configuration window.
-///
-
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-import org.julialang
diff --git a/src/main.qml b/src/main.qml
index a95a4ee1c1284c394c0fe86876dc87fdef0160a6..3e6f7e5f8c85e0cfe1eaa27bcfb82813ee5a36b3 100644
--- a/src/main.qml
+++ b/src/main.qml
@@ -30,13 +30,21 @@ ApplicationWindow {
 			}
 			Action { text: "&Load Saved State" }
 			Action { text: "&Save Current State" }
-			Action { text: "Save Model &Output" }
 			MenuSeparator { }
 			Action {
 				text: "&Quit"
 				onTriggered: { mainWindow.close() }
 			}
 		}
+		Menu {
+			title: "&Data"
+			Action {
+				text: "Show &Population Graph"
+				onTriggered: { populationGraph.visible = true }
+			}
+			Action { text: "Save &Graphical Output" }
+			Action { text: "Save CSV Output" }
+		}
 		Menu {
 			title: "&Help"
 			Action {
@@ -53,69 +61,12 @@ ApplicationWindow {
 			}
 		}
 	}
-	
-	MessageDialog {
-		id: aboutDialog
-		text: "Persefone.jl Desktop"
-		informativeText: "A mechanistic model of agricultural landscapes \
-and ecosystems in Europe.\n\n\
-© 2023 Daniel Vedder, Lea Kolb, Guy Pe'er\n\
-Distributed under the MIT license."
-	}
 
-	RowLayout {
-		spacing: 6
+	// visualise the model map and the locations of animals
+	MakieViewport {
+		id: mapviewport
 		anchors.fill: parent
-
-		// visualise the model map and the locations of animals
-		MakieViewport {
-			id: mapviewport
-			Layout.fillWidth: true
-			Layout.fillHeight: true
-			renderFunction: render_map_callback
-		}
-
-		ColumnLayout {
-
-			// the calendar is no longer a single widget, but needs to be
-			// constructed manually :-(
-			ColumnLayout {
-				id: calenderWidget
-				Layout.alignment: Qt.AlignHCenter
-				Text {
-					text: "<b>"+Qt.formatDate(new Date(), "MMM yyyy")+"</b>" //FIXME
-				}
-				DayOfWeekRow {
-					locale: grid.locale
-					Layout.column: 1
-					Layout.fillWidth: true
-				}
-				MonthGrid {
-					id: grid
-					//month: Calendar.December //FIXME
-					//year: 2015 //FIXME
-					locale: Qt.locale("en_GB")
-					Layout.fillWidth: true
-					Layout.fillHeight: true
-					delegate: Text {
-						horizontalAlignment: Text.AlignHCenter
-						verticalAlignment: Text.AlignVCenter
-						opacity: model.month === grid.month ? 1 : 0
-						text: model.today ? "<b>"+model.day+"</b>" : model.day
-						font: grid.font
-						required property var model
-					}
-				}
-			}
-			
-			// plot the population sizes over time
-			MakieViewport {
-				id: plotviewport
-				Layout.fillWidth: true
-				Layout.fillHeight: true
-				renderFunction: render_plot_callback //TODO
-			}
-		}
+		renderFunction: render_map_callback
 	}
 
 	// the main control bar, with pause/step/run buttons, the progress
@@ -169,6 +120,34 @@ Distributed under the MIT license."
 				ToolTip.visible: hovered
 				onValueChanged: vars.delay = value
 			}
+			Text {
+				id: dateText
+				text: Julia.datestring()
+			}
+		}
+	}
+
+	// extra windows
+	MessageDialog {
+		id: aboutDialog
+		text: "Persefone.jl Desktop"
+		informativeText: "A mechanistic model of agricultural landscapes \
+and ecosystems in Europe.\n\n\
+© 2023 Daniel Vedder, Lea Kolb, Guy Pe'er\n\
+Distributed under the MIT license."
+	}
+
+	Window {
+		id: populationGraph
+		title: "Population Graph"
+		width: 512
+		height: 512
+		visible: false
+
+		MakieViewport {
+			id: plotviewport
+			anchors.fill: parent
+			renderFunction: render_plot_callback
 		}
 	}
 
@@ -181,8 +160,9 @@ Distributed under the MIT license."
 	JuliaSignals {
 		signal updateMakie()
 		onUpdateMakie: {
+			dateText.text = Julia.datestring();
 			mapviewport.update();
-			plotviewport.update();
+			//plotviewport.update();
 		}
 	}
 
diff --git a/src/splash.qml b/src/splash.qml
index b731adc71d4b26c1a9377f511d62248328513d19..f7f3ca1d05061802791e02c767e5441836e835ad 100644
--- a/src/splash.qml
+++ b/src/splash.qml
@@ -8,12 +8,12 @@ import QtQuick.Controls
 import org.julialang
 
 Rectangle {
-	width: 1200
-	height: 500
+	width: 600
+	height: 250
 	
     Image {
         source: "persefonejl_logo_v3_splash.png"
-		fillMode: Image.PreserveAspectFit
+		fillMode: Image.Stretch
     }
     Timer {
 		// Show the splash screen while the model object is initialised