I'd like to be able to create a countdown timer (example: 20 minutes and then the timer beeps) for an R Shiny app that I'm working on that the user can start, stop, and reset by clicking start/stop/reset buttons. I've seen a few examples of countdowns that count towards a specific date/time, but haven't come across one for a generic timer. Is something like this possible? Any help would be appreciated!
Asked
Active
Viewed 4,792 times
5
-
Try this https://shiny.rstudio.com/gallery/timer.html – Pork Chop Mar 13 '18 at 08:04
-
I looked at that, but I need the timer to go down, not up, and I'd like for the user to be able to start and stop it by clicking a button. – rossdrucker9 Mar 13 '18 at 08:31
1 Answers
17
Here is one possible approach. We use two reactiveVal
's, one to keep track of the time left, and one to store if the timer is currently active or not.
Hope this helps!
library(lubridate)
library(shiny)
ui <- fluidPage(
hr(),
actionButton('start','Start'),
actionButton('stop','Stop'),
actionButton('reset','Reset'),
numericInput('seconds','Seconds:',value=10,min=0,max=99999,step=1),
textOutput('timeleft')
)
server <- function(input, output, session) {
# Initialize the timer, 10 seconds, not active.
timer <- reactiveVal(10)
active <- reactiveVal(FALSE)
# Output the time left.
output$timeleft <- renderText({
paste("Time left: ", seconds_to_period(timer()))
})
# observer that invalidates every second. If timer is active, decrease by one.
observe({
invalidateLater(1000, session)
isolate({
if(active())
{
timer(timer()-1)
if(timer()<1)
{
active(FALSE)
showModal(modalDialog(
title = "Important message",
"Countdown completed!"
))
}
}
})
})
# observers for actionbuttons
observeEvent(input$start, {active(TRUE)})
observeEvent(input$stop, {active(FALSE)})
observeEvent(input$reset, {timer(input$seconds)})
}
shinyApp(ui, server)

Florian
- 24,425
- 4
- 49
- 80
-
Florian this is a really nice solution, thanks - I had to do something similar. Out of interest, when you use something like 'invalidate after'. Let's say you wanted something more finegrained and reduced the number from 1000 to something much shorter. Are there any kind of performance costs to having the function like this repeatedly and quickly invalidating and rechecking the conditions, or is even invalidating every 10ms or so easy for a shiny app to handle? – Jamie Jul 19 '21 at 18:38
-
1@Jamie That depends on the computational complexity of the function. If it's something as simple as this, I do not expect it to be a problem. However, over a longer period of time, the timer might become less accurate since it adds a little bit of computation time every 10ms. In that case, it would be better to store the system time when the timer starts, and calculate the 'time left' as the difference between the stored time and the current system time. Hope this helps! – Florian Jul 20 '21 at 09:36