Skip to contents

Update any combination of character, mood, status, bubble, size, placement, theme, motion, click and gesture from your Shiny server. Arguments left as NULL stay unchanged. Invalid values warn and leave the mascot unchanged. In Shiny development mode they also show a worried mascot with the problem as its status; the next valid update clears it. Updates sent before a mascot appears are kept until it appears.

Usage

update_mascot(
  id,
  ...,
  character = NULL,
  mood = NULL,
  status = NULL,
  bubble = NULL,
  size = NULL,
  placement = NULL,
  theme = NULL,
  motion = NULL,
  click = NULL,
  gesture = NULL,
  session = shiny::getDefaultReactiveDomain()
)

Arguments

id

A required non-empty string without whitespace, unique on the page. In a Shiny module use ns("helper") in the UI and "helper" in the server; update_mascot() adds the module namespace once.

...

Must be empty. Forces the arguments after it to be named.

character

A character id such as "tessa", or a mascot_character() helper. NULL (the default) leaves the character unchanged.

mood

A mood name or mascot_mood() object. NULL (the default) leaves the mood unchanged.

status

A non-empty string or mascot_status() object. NULL (the default) leaves the status unchanged; mascot_status() hands the status back to the mood. Text is always escaped.

bubble

A non-empty string, an htmltools tag or tag list, or a mascot_bubble() object. NULL (the default) leaves the bubble unchanged; mascot_bubble() clears it. Plain text is escaped; htmltools::HTML() or tags supply HTML. Tags with dependencies or head content are not supported.

size

A pixel number, percentage string or mascot_size() object, as in mascot(). NULL (the default) leaves the size unchanged.

placement

A shortcut, named list or mascot_placement() object, as in mascot(). NULL (the default) leaves placement unchanged.

  • Size and placement changes keep the running mascot, its mood and its character.

  • A new placement replaces any position the visitor dragged the mascot to.

  • Floating placement shows a minimise control by default, and dragging is off.

  • Inline placement ignores minimise and drag. If you set either one yourself, a message says so; the defaults give no message. The update is still sent.

  • With dragging enabled, the minimised restore button can also be dragged. A click restores it; a drag leaves it minimised in its new position.

theme

A theme in the forms accepted by mascot(). NULL (the default) leaves the theme unchanged; "original" or mascot_theme() restores the character's own colours. A palette id string chooses a named palette. After a character swap, the theme is applied again to the new character's parts. The palette carries over if the new character has one of that name. If it has not, you get a warning with class mascot_palette_dropped, and colours you set explicitly stay for the roles both characters share.

motion

A logical flag or mascot_motion_settings() object, as in mascot(). NULL (the default) leaves motion unchanged. Changes apply to the live mascot without restarting it; the visitor's reduced-motion preference still applies.

click

A label, a mascot_action() object or a mascot_popup_menu() object, as in mascot(). NULL (the default) leaves the click unchanged; an empty helper, mascot_action() or mascot_popup_menu(), removes it, so the mascot is a picture again. A new click replaces the old one completely and closes an open menu. Clicks and menu choices arrive as input$<id>_click and input$<id>_menu in the module that owns the mascot; the click count continues across changes.

gesture

A gesture name or mascot_gesture() object. NULL (the default) sends no gesture. Use "nod" to acknowledge an action or "wave" to greet. Must be named. See mascot_gestures() for descriptions. With gestures = FALSE in mascot_motion_settings(), the gesture is skipped; the mood, bubble and status in the same update still apply. In Shiny development mode, the browser asks Shiny to show a warning that says why.

session

A Shiny session, defaulting to the current reactive domain. Must be named when supplied. NULL outside a server or module is an error.

Value

Called for its side effect; returns NULL invisibly.

Details

In mascot() and its helpers, NULL (where accepted) means none or the default. In update_mascot(), NULL leaves a setting unchanged. To reset a setting, send an empty helper:

SettingReset sent to update_mascot()
Charactercharacter = mascot_character() (back to "tessa")
Moodmood = mascot_mood() (back to "idle")
Sizesize = mascot_size() (160, small_screen = "auto")
Custom statusstatus = mascot_status() follows the mood
Bubblebubble = mascot_bubble() removes it
Themetheme = mascot_theme() restores character colours
Clickclick = mascot_action() (or mascot_popup_menu()) removes it
Placementplacement = mascot_placement() (bottom-right); mascot_placement("inline") for inline
Motionmotion = mascot_motion_settings() allows movement unless the visitor prefers reduced motion

Each non-NULL argument replaces that whole setting, not part of it. For example, update_mascot("h", size = 200) also resets a custom small_screen to "auto".

mascot() and the helpers raise an error on an invalid value. update_mascot() warns instead, with class mascot_update_ignored, and leaves the mascot unchanged. That also covers a helper that raises a mascot_error while an argument is evaluated inside the call, such as size = mascot_size(input$px); the whole update is then ignored. An invalid id, unknown arguments in ... or a missing session still raise errors. See mascot-package for all condition classes.

An unknown palette id is an error in mascot() but only a warning in update_mascot(). mascot() runs while the UI is built, where an error shows at once. update_mascot() runs in a live session, where an error would stop the app for the visitor, so it warns and the mascot keeps running unchanged. The message names any other characters that have a palette of that name. (A palette carried over from before a character swap is dropped instead, with a warning of class mascot_palette_dropped; see the theme argument.)

Status text and bubbles. Use the always visible status line for a short description of the current state. Use a bubble for a message about something that happened; it may time out. Use mascot_bubble(..., duration = Inf) for a message the user must act on, such as an invalid form, because a timed message can vanish before it is read. Bubbles survive mood changes and swaps; a queued bubble's timer starts when it appears. See mascot_bubble() for timing and closing behaviour.

Shiny modules. In a module, use mascot(ns("helper")) in the UI and update_mascot("helper") in its server. A session$ns("helper") id is also accepted, including in nested modules; the namespace is not added twice. An invalid id type, missing session or extra argument is an error. Calling with all update arguments NULL does nothing.

Gestures. A character or mood change applies before the gesture. During a character swap the gesture is skipped, because the wave within the swap replaces it. Reduced motion skips movement. The latest gesture sent before the mascot appears plays once on appearance, after its queued character and mood are applied.

React to your app's state. Call update_mascot() from an observer. Shiny sends updates when the observer finishes, so for a mood to show while a long computation runs, run it as a shiny::ExtendedTask and follow its status:

task <- ExtendedTask$new(function(x) promises::future_promise(fit_model(x)))
observeEvent(input$run, {
  update_mascot("helper", mood = "thinking", status = "Fitting the model")
  task$invoke(data())
})
observe({
  switch(task$status(),
    success = update_mascot("helper", mood = "happy", status = "Model ready"),
    error = update_mascot("helper", mood = "worried", status = "The model failed")
  )
})

For form validation, observe whether the inputs are valid (yourself, or with a validation package such as shinyvalidate) and set the mood from it:

observe({
  valid <- grepl("@", input$email, fixed = TRUE)
  update_mascot(
    "helper",
    mood = if (valid) "happy" else "worried",
    status = if (valid) "Ready to submit" else "Check the email address"
  )
})

Queued updates. Updates sent before a mascot appears are kept for the session, also when the first mascot is created by shiny::renderUI() or shiny::insertUI(). Each call is checked as a whole before its latest values are accepted. Until the character is known, a buffer of limited size holds whole calls, and they are checked against the first character that appears. A call that fails the check adds nothing, not even its bubble or status. A mascot that replaces one with the same id gets the latest accepted state, and only the latest pending gesture plays, once. A bubble the visitor dismissed stays dismissed in the replacement; sending a bubble again starts a new one, even with the same text.

Several calls in one flush. A flush is one round of Shiny reacting to inputs. Shiny sends the mascot one message when it ends, built from every call made in it, setting by setting: a call changes only the settings it names, and when two calls name the same setting the later call wins. If a reaction to a click sets mood = "happy" and a state observer sets mood = "worried" in the same flush, the observer that runs last decides the face and the other mood is never shown. status and bubble work the same way, and only the latest gesture plays. Give each setting one owner, so no call can overwrite another's:

# The event reaction owns the bubble and gesture, the observer owns the mood.
observeEvent(input$save, {
  update_mascot("helper", bubble = "Saved", gesture = "nod")
})
observe({
  update_mascot("helper", mood = if (valid()) "idle" else "worried")
})

After a custom status. A custom status stays until you replace it or hand it back with status = mascot_status(). A call that sets only mood changes the face and leaves the status text as it was, so "Fitting the model" can sit beside a happy face. To let the status follow the mood again, send update_mascot("helper", mood = "happy", status = mascot_status()).

What a re-created mascot takes from its UI call. A mascot that shiny::renderUI() draws again, or that is removed and added back, receives the latest accepted state (see "Queued updates"). For each setting an earlier accepted call to update_mascot() has set, the value in the mascot() call is ignored; the settings the server never set come from the mascot() call. A mood, status or size in the UI call therefore only matters until the server first sets that setting, and after that the UI call can use the defaults. A call that changes character also sets the mood (adapted to the new character), so after it the UI call's mood is ignored too.

A mascot in a modal. The mascot is off the page while the modal is closed. Updates sent then are kept, as under "Queued updates", and applied when the modal opens: the last value of each setting, a bubble sent in the meantime, and a gesture, which plays once as the mascot appears. So it can open with the mood, status or an unfinished bubble from the last visit, or with whatever was sent while the modal was closed. To open it clean, reset those settings as the modal opens (a reset is an update, so it also replaces the UI call's values for them) and send no gesture while it is closed. A reset cannot cancel a gesture already sent. Sending updates only while the modal is open avoids what was sent while it was closed, but not the state left from the last visit.

observeEvent(input$help, {
  update_mascot(
    "helper",
    mood = mascot_mood(),
    status = mascot_status(),
    bubble = mascot_bubble()
  )
  showModal(modalDialog(mascot("helper"), title = "Help"))
})

See also

mascot(), mascot_motion_settings() for a Shiny example that switches idle movement and plays a nod.

Other mascots in Shiny: mascot()

Examples

if (FALSE) { # interactive()
shiny::shinyApp(
  ui = shiny::fluidPage(
    mascot("helper"),
    shiny::actionButton("work", "Think"),
    shiny::actionButton("done", "Finish")
  ),
  server = function(input, output, session) {
    shiny::observeEvent(input$work, {
      update_mascot("helper", mood = "thinking", status = "Working")
    })
    shiny::observeEvent(input$done, {
      update_mascot("helper", mood = "happy", status = mascot_status(),
        bubble = "Finished", gesture = "nod")
    })
  }
)
}