diff --git a/scripts/larvatagger-gui.jl b/scripts/larvatagger-gui.jl
index 1bc01e8db4d536ee67bbd9cdbfb5144ce6954b03..8cadce79670ac92b648fccb5e093f32ec6ffa402 100755
--- a/scripts/larvatagger-gui.jl
+++ b/scripts/larvatagger-gui.jl
@@ -9,7 +9,7 @@ fi
 FLAGS=
 if [ "$1" = "--sysimage" -o "$1" = "-J" ]; then FLAGS="--sysimage $2 "; shift 2; fi
 if [ "${1:0:2}" = "-J" ]; then FLAGS="$1 "; shift; fi
-if [ -n "$1" -a -f "$1" ]; then FLAGS="$FLAGS -iq "; fi
+if [ -n "$1" -a -f "$1" -o "$1" = "blank" ]; then FLAGS="$FLAGS -iq "; fi
 if [ -z "$JULIA" ]; then JULIA=julia; fi
     exec $JULIA --project="$PROJECT_DIR" --color=yes --startup-file=no $FLAGS\
     "${BASH_SOURCE[0]}" "$@"
diff --git a/src/cli_open.jl b/src/cli_open.jl
index 28bd83a8d5d4677ac3c4f6bd2aa7b3f92628d123..c72ecf9bea881fd7afd4f1699b87d3abd3da1fb3 100644
--- a/src/cli_open.jl
+++ b/src/cli_open.jl
@@ -47,7 +47,9 @@ function main(args=ARGS; exit_on_error=false)
 
     verbose = !parsed_args["--quiet"]
     infile = parsed_args["<file-path>"]
-    if !isfile(infile)
+    if infile == "blank"
+        infile = nothing
+    elseif !isfile(infile)
         @error "File not found; did you specify a file path?" infile
         exit()
     end
diff --git a/src/models.jl b/src/models.jl
index 2c537415dc328090bbf2133ac7e5b982be09c88c..9910a28cadded6c41161031c487c93d567b6fc9e 100644
--- a/src/models.jl
+++ b/src/models.jl
@@ -310,7 +310,8 @@ end
 
 medianlarvasize(larvae::Dict; n::Int=100) = medianlarvasize(collect(values(larvae)); n=n)
 
-function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)
+function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)::Float32
+    isempty(larvae) && return 0.0f0
     larvae = Random.shuffle(larvae)
     sizes = Float64[]
     sizehint!(sizes, n)
@@ -324,7 +325,8 @@ function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)
     return median(sizes)
 end
 
-function simultaneouslarvae(larvae)
+function simultaneouslarvae(larvae)::Int
+    isempty(larvae) && return 0
     laststep = maximum([larva.alignedsteps[end] for larva in values(larvae)])
     n = 0
     for step in 1:20:laststep
diff --git a/src/plots.jl b/src/plots.jl
index e3bbceda9e7f2fe27c8495e4055a65d4fe04eab5..4a76a07e37abd5b8b02ec19cc83924fb95b49504 100644
--- a/src/plots.jl
+++ b/src/plots.jl
@@ -588,12 +588,19 @@ function DecoratedLarvae(larvae::Vector{DecoratedLarva})
             end
         end
     end
-    center = Meshes.coordinates ∘ Meshes.centroid
-    centers = cat((center(larva.activearea) for larva in larvae)...; dims=2)
-    norm2 = sum(centers .* centers, dims=1)
+    centers = zeros(Float32, (2, 0))
+    norm2 = zeros(Float32, (1, 0))
+    if ~isempty(larvae)
+        center = Meshes.coordinates ∘ Meshes.centroid
+        centers = cat((center(larva.activearea) for larva in larvae)...; dims=2)
+        norm2 = sum(centers .* centers, dims=1)
+    end
     DecoratedLarvae(larvae, centers, norm2, hovered_larva, hovering_active)
 end
 
+Base.isempty(larvae::DecoratedLarvae) = isempty(larvae.larvae)
+Base.length(larvae::DecoratedLarvae) = length(larvae.larvae)
+
 function find(larvae::DecoratedLarvae, position)
     pos = Vector(position)'
     p2 = pos * pos'
@@ -630,7 +637,13 @@ end
 
 Meshes.boundingbox(larva::StatefulLarva) = Meshes.boundingbox(larva.model)
 Meshes.boundingbox(larva::DecoratedLarva) = Meshes.boundingbox(larva.larva)
-Meshes.boundingbox(larvae::DecoratedLarvae) = Meshes.boundingbox(map(Meshes.boundingbox, larvae.larvae))
+function Meshes.boundingbox(larvae::DecoratedLarvae)
+    if isempty(larvae)
+        Meshes.Box(Meshes.Point(0.0, 1.0), Meshes.Point(0.0, 1.0))
+    else
+        Meshes.boundingbox(map(Meshes.boundingbox, larvae.larvae))
+    end
+end
 
 function setmouseevents!(scene, plot::DecoratedLarvae, ctrl; consume=false)
     on(events(scene).mousebutton) do mb
diff --git a/src/viewer.jl b/src/viewer.jl
index 4175e5fa695a84ba1e712a025e6f389a263d5cef..b2344b44c0937f0cb60ae6270712619a7ce975aa 100644
--- a/src/viewer.jl
+++ b/src/viewer.jl
@@ -1,45 +1,52 @@
 
 struct ViewerView
     assayplot::AssayViewer
-    trackplot::TrackViewer
+    trackplot::Union{Nothing, TrackViewer}
 end
 
 function Bonito.jsrender(session::Session, vv::ViewerView)
-    controller = vv.trackplot.plot.controller
     delayed_controller = vv.assayplot.plot.controller
 
-    assaydom = r(session, vv.assayplot)
-    trackdom = r(session, vv.trackplot)
-
-    on(session, larvaevents(controller).activated) do _
-        vv.assayplot.visible[] = false
-        evaljs(session,
-               js"""
-               const assayviewer = $(assaydom);
-               const trackviewer = $(trackdom);
-               assayviewer.style.display = "none";
-               trackviewer.style.display = "block";
-               """)
-    end
-    on(session, larvaevents(controller).deactivated) do _
-        vv.assayplot.visible[] = true
-        evaljs(session,
-               js"""
-               const assayviewer = $(assaydom);
-               const trackviewer = $(trackdom);
-               trackviewer.style.display = "none";
-               assayviewer.style.display = "block";
-               """)
-    end
-
     on(session.on_close) do closed
         if closed
             pause(delayed_controller.player)
         end
     end
 
-    r(session,
-      DOM.div(Bonito.TailwindCSS, assaydom, trackdom; class="flex flex-row"))
+    assaydom = r(session, vv.assayplot)
+    trackdom = nothing
+
+    if !isnothing(vv.trackplot)
+        controller = vv.trackplot.plot.controller
+        trackdom = r(session, vv.trackplot)
+
+        on(session, larvaevents(controller).activated) do _
+            vv.assayplot.visible[] = false
+            evaljs(session,
+                js"""
+                const assayviewer = $(assaydom);
+                const trackviewer = $(trackdom);
+                assayviewer.style.display = "none";
+                trackviewer.style.display = "block";
+                """)
+        end
+        on(session, larvaevents(controller).deactivated) do _
+            vv.assayplot.visible[] = true
+            evaljs(session,
+                js"""
+                const assayviewer = $(assaydom);
+                const trackviewer = $(trackdom);
+                trackviewer.style.display = "none";
+                assayviewer.style.display = "block";
+                """)
+        end
+
+        r(session,
+          DOM.div(Bonito.TailwindCSS, assaydom, trackdom; class="flex flex-row"))
+
+    else
+        r(session, DOM.div(Bonito.TailwindCSS, assaydom))
+    end
 end
 
 function larvaviewer(path::String; allow_multiple_tags::Union{Nothing, Bool}=false,
@@ -68,9 +75,14 @@ function larvaviewer(controller;
         multipletags::Union{Nothing, Bool}=false,
         viewfactor::Real=1,
     )
-    model = getlarvae(controller)
-    times = gettimes(controller)
-    tag_lut = gettags(controller)
+    model = LarvaModel[]
+    times = PlanarLarvae.Time[0.0, 1.0]
+    tag_lut = Observable(TagLUT())
+    if haskey(controller, :larva)
+        model = getlarvae(controller)
+        times = gettimes(controller)
+        tag_lut = gettags(controller)
+    end
 
     controller[:player] = player = timecontroller(times)
     controller[:larva] = larva = LarvaController(controller, model, tag_lut, player)
diff --git a/src/wgl.jl b/src/wgl.jl
index 69984ede68c42d00af023633e122816b22dab1df..c21d40fc2d8abf281f35b0a8be23e0374dcfbc00 100644
--- a/src/wgl.jl
+++ b/src/wgl.jl
@@ -90,9 +90,11 @@ function AssayPlot(ctrl, larvae::DecoratedLarvae; size=FIGSIZE)
     ax = Axis(fig.layout[1, 1], aspect=DataAspect(), xgridwidth=width, ygridwidth=width,
               xgridcolor=color, ygridcolor=color)
     autosize!(ax, size)
-    plot = larvaplot!(ax, larvae)
-    setkeyboardevents!(plot.parent, getplayer(ctrl))
-    setbounds!(ax, ctrl, larvae)
+    if !isempty(larvae)
+        plot = larvaplot!(ax, larvae)
+        setkeyboardevents!(plot.parent, getplayer(ctrl))
+        setbounds!(ax, ctrl, larvae)
+    end
     button = homebutton()
     on(button) do _
         setbounds!(ax, ctrl, larvae)
@@ -845,6 +847,7 @@ function trackviewer(controller;
         kwargs...
     )
     model = getlarvae(controller)
+    isempty(model) && return nothing
     plot = trackplot(model, controller; editabletags=editabletags, kwargs...)
     play = player(controller)
     sele = tagselector(controller; editabletags=editabletags, multipletags=multipletags)