diff --git a/Project.toml b/Project.toml
index 1b5dd3db1655b9b9c4b897d22d63728e669243f6..492c4be584ceb11bfbbf6c8adbd5a1275249f6fc 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "LarvaTagger"
 uuid = "8b3b36f1-dfed-446e-8561-ea19fe966a4d"
 authors = ["François Laurent", "Institut Pasteur"]
-version = "0.4.0"
+version = "0.5.0"
 
 [deps]
 Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
diff --git a/src/controllers.jl b/src/controllers.jl
index ee7c308af3dad46fa144547da094a6772c21f4de..3b4022ba6a71b5a2b07fd155e1eec9d1f74174a7 100644
--- a/src/controllers.jl
+++ b/src/controllers.jl
@@ -216,6 +216,7 @@ setboundingbox!(c::LarvaController, bb) = (c.boundingbox[] = bb)
 getmedianlarvasize(c) = getmedianlarvasize(gethub(c)[:larva])
 getmedianlarvasize(c::LarvaController) = c.medianlarvasize
 
+getplayer(c::AbstractAnimator) = c
 getplayer(c::LarvaController) = c.player
 getplayer(c) = getplayer(gethub(c))
 getplayer(hub::ControllerHub) = haskey(hub, :player) ? hub[:player] : getplayer(hub[:larva])
diff --git a/src/players.jl b/src/players.jl
index 6191b9748f2d8b92fba8a0bcdba9844c0ec6c55e..d4bea8b78447b77c2c4d2ac75762f4c6aa3b1b45 100644
--- a/src/players.jl
+++ b/src/players.jl
@@ -117,6 +117,24 @@ play!(controller::TimeController) = (controller.playing[] = true)
 pause!(controller::TimeController) = (controller.playing[] = false)
 playpause!(controller::TimeController) = (controller.playing[] = !controller.playing[])
 
+function stepforward!(controller::TimeController)
+    step = controller.timestep[]
+    if step < controller.stepmax[]
+        controller.timestep[] = step + 1
+    elseif isplaying(controller)
+        pause!(controller)
+    end
+end
+
+function stepbackward!(controller::TimeController)
+    step = controller.timestep[]
+    if controller.stepmin[] < step
+        controller.timestep[] = step - 1
+    elseif isplaying(controller)
+        pause!(controller)
+    end
+end
+
 function timecontroller(times::Vector{Float64}; speed=1.1)
     issorted(times) || throw(ArgumentError("timestamps are not sorted"))
     initialstep = 1
diff --git a/src/plots.jl b/src/plots.jl
index 72ecf6082adaa4011df24bd849dac577842f46de..ba7fe2820a65525fc796027fcc7803e975f40d72 100644
--- a/src/plots.jl
+++ b/src/plots.jl
@@ -300,7 +300,8 @@ function assign_tag_to_segment!(larvaview, firststep)
     flag_active_larva_as_edited(controller)
 end
 
-function setmouseevents!(scene, larva::SingleLarvaView; blocking=true, priority=Int8(10), kwargs...)
+function setmouseevents!(scene, larva::SingleLarvaView;
+        blocking=true, priority=Int8(10), kwargs...)
     # WGLMakie does not support addmouseevents! with plots as additional input args
     mouseevents = Makie.addmouseevents!(scene; priority=priority)
 
@@ -406,6 +407,7 @@ function Makie.plot!(plot::LarvaPlot{Tuple{SingleLarvaView}})
     larva = plot[1][]
     scene = plot.parent
     setmouseevents!(scene, larva)
+    setkeyboardevents!(scene, larva)
     larvaplot!(plot, larva.path, larva.outline, larva.outline_color, larva.visible)
     if iseditable(larva)
         lines!(plot, larva.segment; color=larva.segment_color, linewidth=6, visible=larva.segment_visible)
@@ -413,6 +415,20 @@ function Makie.plot!(plot::LarvaPlot{Tuple{SingleLarvaView}})
     end
 end
 
+setkeyboardevents!(scene::Scene, larva::SingleLarvaView) = setkeyboardevents!(scene, larva.controller)
+function setkeyboardevents!(scene::Scene, controller)
+    player = getplayer(controller)
+    on(events(scene).keyboardbutton) do event
+        if event.action == Keyboard.press
+            if event.key in (Keyboard.left, Keyboard.down)
+                stepbackward!(player)
+            elseif event.key in (Keyboard.right, Keyboard.up)
+                stepforward!(player)
+            end
+        end
+    end
+end
+
 ##
 
 struct DecoratedLarva
diff --git a/src/wgl.jl b/src/wgl.jl
index 7cd88e604beb0479c75b09294e0a4611847cf99f..e968a4ba15564703bc7df9e9d81738362bf075a1 100644
--- a/src/wgl.jl
+++ b/src/wgl.jl
@@ -124,7 +124,8 @@ 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)
-    larvaplot!(ax, larvae)
+    plot = larvaplot!(ax, larvae)
+    setkeyboardevents!(plot.parent, getplayer(ctrl))
     setbounds!(ax, ctrl, larvae)
     button = homebutton()
     on(button.click) do _
@@ -239,10 +240,7 @@ end
 function stepforwardbutton(ctrl::TimeController; kwargs...)
     button = simpletrigger(StepForwardButton, with_attributes(kwargs; class=css_button))
     on(button.click) do _
-        step = ctrl.timestep[]
-        if step < ctrl.stepmax[]
-            ctrl.timestep[] = step + 1
-        end
+        stepforward!(ctrl)
     end
     return button
 end
@@ -260,10 +258,7 @@ end
 function stepbackwardbutton(ctrl::TimeController; kwargs...)
     button = simpletrigger(StepBackwardButton, with_attributes(kwargs; class=css_button))
     on(button.click) do _
-        step = ctrl.timestep[]
-        if ctrl.stepmin[] < step
-            ctrl.timestep[] = step - 1
-        end
+        stepbackward!(ctrl)
     end
     return button
 end
@@ -362,6 +357,7 @@ function JSServe.jsrender(session::Session, p::Player)
               r(session, p.stepforwardbutton),
               r(session, p.timeslider),
               r(session, p.timelabel);
+              onmouseenter=js"LarvaTagger.focusOnTimeSlider(this)",
               p.attributes...
              ))
 end