<br><br><br><br> .leftcol[ ## Introducing {surveydown} ### An open-source platform for programmable, markdown-based surveys <br> **John Paul Helveston**<br>George Washington University posit::conf(2025) ] .rightcol[ <center> <img src="images/logo-surveydown.png" width=50%> </center> ] --- background-color: #000 class: center, middle, inverse # I try to figure out what people want <center> <img src="images/crystal_ball.jpg" width=500> </center> --- class: center, middle <center> <img src="images/demo.gif" width=80%> </center> --- class: center, middle ### WYSIWYG Interface <center> <img src="images/google-form.png" width=80%> </center> --- class: middle .leftcol65[ <center> <img src="images/google-form.png" width=100%> </center> ] .rightcol35[ # Limitations <br> ## .font80[❌] .font90[Reproducibility] ] --- class: middle .leftcol65[ <center> <img src="images/google-form.png" width=100%> </center> ] .rightcol35[ # Limitations <br> ## .font80[❌] .font90[Reproducibility] ## .font80[❌] .font90[Version control] ] --- <center> <img src="images/xkcd-version-control.png" width=40%> </center> --- class: middle .leftcol65[ <center> <img src="images/google-form.png" width=100%> </center> ] .rightcol35[ # Limitations <br> ## .font80[❌] .font90[Reproducibility] ## .font80[❌] .font90[Version control] ## .font80[❌] .font90[Limited features] ] --- class: middle .leftcol65[ <center> <img src="images/google-form.png" width=100%> </center> ] .rightcol35[ # Limitations <br> ## .font80[❌] .font90[Reproducibility] ## .font80[❌] .font90[Version control] ## .font80[❌] .font90[Limited features] ## .font80[❌] .font90[Open source] ] --- .leftcol60[ <br><br> <center> <img src="images/logo-quarto.png" width=50%> </center> ] .rightcol40[ <br> # ~~Limitations~~ Features <br> ## ✅ Reproducibility ## ✅ Version control ## ✅ Lots of Features ## ✅ Open source ] --- class: center, middle, inverse # Wouldn't it be nice if there was a<br>"Quarto" for surveys? --- class: middle, inverse # .font130[.center[surveydown to the rescue!]] <br> <center> <img src="images/logo-surveydown.png" width=300> </center> --- class: middle .leftcol40[ # In this talk,<br>you'll learn: ] .rightcol60[ ## - What is `surveydown`? ## - How does it work? ## - What can I do with it? ## - What's next? ] --- class: center, middle, inverse # .font140[.center[What is surveydown?]] <br> <center> <img src="images/logo-surveydown.png" width=300> </center> --- background-image: url("images/images/Slide1.png") background-size: cover --- .leftcol55[ <center> <img src="images/survey-code1.png" width=100%> </center> ] .rightcol45[ .center[`survey.qmd` --> Rendered survey] <center> <img src="images/survey-app.png" width=100%> </center> ] --- .leftcol55[ <center> <img src="images/survey-code2.png" width=100%> </center> ] .rightcol45[ <br><br> ### .center[Standard YAML header<br>w/options for "clean" output] ] --- .leftcol55[ <center> <img src="images/survey-code3.png" width=100%> </center> ] .rightcol45[ <br><br><br><br><br> ### .font90[.center[Load the `surveydown` package]] ] --- .leftcol55[ <center> <img src="images/survey-code4.png" width=100%> </center> ] .rightcol45[ <br><br><br><br><br><br><br><br><br><br><br> ### .center[Use Quarto fences (`:::`)<br>to define survey pages] ] --- .leftcol55[ <center> <img src="images/survey-code5.png" width=100%> </center> ] .rightcol45[ <br><br><br><br><br><br><br><br><br><br> ### .center[Page content] - Markdown for page content (text, images, etc.) - `sd_question()` for survey questions - `sd_next()` for next button ] --- .leftcol55[ <center> <img src="images/survey-code5.png" width=100%> </center> ] .rightcol45[ .center[`survey.qmd` --> Rendered survey] <center> <img src="images/survey-app.png" width=100%> </center> ] --- .leftcol55[ <center> <img src="images/survey-code6.png" width=100%> </center> ] .rightcol45[ .center[`survey.qmd` --> Rendered survey] <center> <img src="images/survey-app.png" width=80%> <img src="images/data-preview.png" width=45%> </center> ] --- class: middle, inverse # .center[Wait a minute...<br>Quarto renders to _static_ html pages, right?] <center> <img src="images/batman-think.gif" width=40%> </center> --- class: middle, inverse # .font140[.center[Shiny to the rescue!]] <br> .leftcol[ <center> <img src="images/logo-shiny.png" width=60%> </center> ] .rightcol[ <center> <img src="images/batman-thumbs-up.gif" width=70%> </center> ] --- background-image: url("images/images/Slide2.png") background-size: cover --- background-image: url("images/images/Slide3.png") background-size: cover --- background-image: url("images/images/Slide4.png") background-size: cover --- class: center, middle # A complete `surveydown` survey <br> .leftcol45[ ### `survey.qmd` .font120[A **Quarto doc** defining the survey content (pages, text, images, questions, etc).] ] .rightcol55[ ### `app.R` .font120[An **R script** defining the<br>survey Shiny app.] ] --- class: center, middle, inverse # Don't panic! <center> <img src="images/spiderman-oh-no.gif" width=80%> </center> --- class: middle .leftcol65[.code90[ Typical `app.R` file ``` r library(surveydown) ui <- sd_ui() server <- function(input, output, session) { sd_server() } shiny::shinyApp(ui = ui, server = server) ``` ]] --- class: middle .leftcol65[.code90[ Typical `app.R` file ``` r library(surveydown) *ui <- sd_ui() server <- function(input, output, session) { * sd_server() } shiny::shinyApp(ui = ui, server = server) ``` ]] .rightcol35[ <br><br><br>Render the `survey.qmd` file <br> Run the surveydown server ] --- class: middle, inverse # .center[Wait a minute...<br>How do you store the response data?] <center> <img src="images/superman-think.gif" width=50%> </center> --- class: middle, inverse # .font140[.center[PostgreSQL to the rescue!]] .leftcol[ <br> <center> <img src="images/logo-postgres.png" width=60%> </center> ] .rightcol[ <img src="images/superman-oh-yeah.gif" width=60%> ] --- class: middle .leftcol65[.code90[ Typical `app.R` file ``` r library(surveydown) *# sd_db_config() *db <- sd_db_connect() ui <- sd_ui() server <- function(input, output, session) { * sd_server(db) } shiny::shinyApp(ui = ui, server = server) ``` ]] .rightcol35[ <br><br><br> Store credentials<br> Connect to the database <br> Pass connection to `sd_server()` ] --- class: center, middle ### supabase.com <center> <img src="images/supabase-table.png" width=95%> </center> --- class: center, middle <center> <img src="images/get-data.png" width=95%> </center> --- class: middle, inverse, center # .font140[What can surveydown do?] --- class: middle ## .center[Surveydown is feature packed!] <center> <img src="images/features.png" width=70%> </center> --- class: middle, inverse .leftcol20[] .rightcol80[ # - Question types # - Conditionally display content # - Conditionally skip forward ] --- class: middle, inverse .leftcol20[] .rightcol80[ # - .green[Question types] # - Conditionally display content # - Conditionally skip forward ] --- class: middle # .center[Question types] .leftcol40[] .rightcol60[ - text - textarea - numeric - mc - mc_multiple - mc_buttons - mc_multiple_buttons - select - slider - slider_numeric - date - daterange - matrix ] --- # .center[Question types: `text`] <br><br><br> .leftcol[ ``` r sd_question( type = "text", id = "fav_hero_name", label = "Who is your favorite super hero?" ) ``` ] .rightcol[ <center> <img src="images/question-text.png" width=100%> </center> ] --- # .center[Question types: `mc`] <br><br> .leftcol[ ``` r sd_question( type = "mc", id = "hero_universe", label = "Which superhero universe do you prefer?", option = c( "Marvel" = "marvel", "DC Comics" = "dc", "Independent/Other" = "other", "I like them all equally" = "all" ) ) ``` ] .rightcol[ <br> <center> <img src="images/question-mc.png" width=100%> </center> ] --- # .center[Question types: `mc_multiple`] <br><br> .leftcol[ ``` r sd_question( type = "mc_multiple", id = "hero_qualities", label = "What qualities do you find most appealing? (Select all that apply)", option = c( "Super strength" = "strength", "Ability to fly" = "flying", "Intelligence/Strategy" = "intelligence", "Humor/Wit" = "humor", "Moral compass" = "morals" ) ) ``` ] .rightcol[ <center> <img src="images/question-mc-multiple.png" width=100%> </center> ] --- # .center[Question types: `mc_buttons`] <br> .leftcol[ ``` r sd_question( type = "mc_buttons", id = "dream_power", label = "If you could have ONE superpower?", option = c( "🕸️ Web-slinging" = "webslinging", "🛡️ Super Strength" = "strength", "✈️ Flight" = "flight", "🧠 Telepathy" = "telepathy", "⚡ Super Speed" = "speed" ), direction = "vertical" ) ``` ] .rightcol[ <center> <img src="images/question-mc-buttons.png" width=100%> </center> ] --- # .center[Question types: `slider_numeric`] <br><br> .leftcol[ ``` r sd_question( type = "slider_numeric", id = "responsibility_scale", label = "How much do you agree with 'With great power comes great responsibility'?", option = 1:10 ) ``` ] .rightcol[ <center> <img src="images/question-slider.png" width=100%> </center> ] --- class: middle, inverse .leftcol20[] .rightcol80[ # - Question types # - .green[Conditionally display content] # - Conditionally skip forward ] --- class: center # Conditionally display content <center> <img src="images/conditional-display.gif" width=50%> </center> --- # .center[Conditionally display content] .leftcol[ survey.qmd ``` r sd_question( type = "mc", id = "has_fav_hero", label = "Do you have a favorite hero?", option = c("Yes" = "yes", "No" = "no") ) sd_question( type = "text", id = "fav_hero", label = "Who is your favorite super hero?" ) ``` ] -- .rightcol[ app.R ``` r sd_show_if( input$has_fav_hero == "yes" ~ "fav_hero" ) ``` Condition ~ Target "If {condition}, then show {target}" ] --- class: inverse, middle .leftcol20[] .rightcol80[ # - Question types # - Conditionally display content # - .green[Conditionally skip forward] ] --- # .center[Conditionally skip forward] <center> <img src="images/screenout.gif" width=60%> </center> --- # .center[Conditionally skip forward] .leftcol[ survey.qmd ```` markdown ::: {.sd_page id=welcome} ```{r} sd_question( type = "mc", id = "has_fav_hero", label = "Do you have a favorite hero?", option = c("Yes" = "yes", "No" = "no") ) ``` ::: ::: {.sd_page id=screenout} # Thank you for your interest ::: ```` ] -- .rightcol[ app.R ``` r sd_skip_forward( input$has_fav_hero == "no" ~ "screenout" ) ``` Condition ~ Target "If {condition}, then skip to {target}" ] --- class: center, middle, inverse # .font140[If it works in Shiny,<br>it works in surveydown] --- class: center, middle .leftcol40[ # Embedding an interactive map with {leaflet} ] .rightcol60[ <center> <iframe src="images/map.mp4" width="762" height="610" frameborder="0" allowfullscreen> </iframe> </center> ] --- class: center, middle ### **Live polling**: Show the response data inside the survey <br> <center> <img src="images/live-polling.gif" width=70%> </center> --- class: center, middle, inverse .leftcol[ <center> <img src="images/deadpool-clap.gif" width=100%> </center> ] .rightcol[ <br><br> # surveydown + LLMs<br>is pretty cool ] --- class: center ## Generate surveys with LLMs! <center> <img src="images/llm.gif" width=80%> </center> --- class: center, middle, inverse # .font150[What's next?] --- class: center, middle # Try out the surveydown [sdstudio package](https://github.com/surveydown-dev/sdstudio/) <br> ## `sdstudio::launch()` --- <center> <img src="images/sdstudio-build.gif" width=90%> </center> --- <center> <img src="images/sdstudio-preview.gif" width=90%> </center> --- <center> <img src="images/sdstudio-data.gif" width=90%> </center> --- class: middle, center, inverse # With great power<br>comes great responsibility --- class: middle # .center[Ways to help:] <br> ## .center[https://github.com/surveydown-dev/surveydown/] <br> .leftcol30[] .rightcol70[ ## - Try it out! ## - Give it a ⭐ ## - Post an issue ## - Join the GitHub discussion ## - Contribute a template ] --- class: inverse <br> .leftcol[ # .center[Thanks!] <br> <center> <img src="images/deadpool-heart.gif" width=70%> </center> ] .rightcol[ ### .center[<span class="white-text">https://surveydown.org/</span>] <br> <center> <img src="images/surveydown-qr.png" width=40%> </center> ] <style> .white-text a { color: white !important; } </style> .footer-large[.white[.right[ @jhelvy.bsky.social
<br> @jhelvy
<br> jhelvy.com
<br> jph@gwu.edu
]]]