-
Notifications
You must be signed in to change notification settings - Fork 4
/
slides.qmd
558 lines (420 loc) · 18.5 KB
/
slides.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
---
title: "Running Shiny without a server"
author: "Joe Cheng"
title-slide-attributes:
data-background-color: white
data-background-image: _extensions/jthomasmock/positslides/assets/backgrounds/sample-title-slide-option-1.png
data-background-size: contain
format:
positslides-revealjs:
width: "1600"
height: "900"
html-math-method: mathjax
filters:
- shinylive
include-before-body:
- _progressbar.html
---
## What is a "server"?
. . .
A computer running 24/7, connected to the internet, that stands ready to run your Shiny app using R or Python.
. . .
Want to share your Shiny app with others? You need a server, and it needs to run R or Python.
## {background-image="images/pi.jpg"}
::: attribution
Photo credit: <a href="https://unsplash.com/@jainath?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jainath Ponnala</a> on <a href="https://unsplash.com/photos/BIHgNEaM394?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
:::
## {background-image="images/towers.jpg"}
::: attribution
Photo credit: <a href="https://unsplash.com/@matthewhenry?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Matthew Henry</a> on <a href="https://unsplash.com/photos/VviFtDJakYk?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
:::
## Lots of great options
- [ShinyApps.io](https://shinyapps.io) for free or paid cloud hosting
- [Posit Connect](https://posit.co/products/enterprise/connect/) for on-premises enterprise hosting
- [Shiny Server](https://posit.co/products/open-source/shinyserver/) for on-premises open source hosting
- [Hugging Face](https://shiny.posit.co/blog/posts/shiny-on-hugging-face/) for free or paid cloud hosting
- [Heroku](https://www.heroku.com/), probably
- ...and more
. . .
But all have tradeoffs between cost, scalability, and features.
## "Academics hate this one simple equation!"
. . .
::: {style="margin-top: 400px"}
$$
\text{Cost} = k \cdot (\text{Computational Complexity} \cdot \text{Number of Users})
$$
:::
## {background-image="images/blewaway.gif" background-size="contain"}
::: sr-only
Wouldn't it be easier if it all just blew away?
:::
# WebAssembly (wasm)
## What is wasm?
::: incremental
- For years, browsers' only native tongue was JavaScript
- Now, browsers all speak JavaScript _and_ wasm
- JavaScript is designed for humans to write, wasm is designed for compilers to emit
- Write code in C/C++, compile to wasm, and run it in the browser!
:::
## I was wrong about wasm!
. . .
"Their" reasoning:
::: incremental
- Shiny is written in R
- R is written in C/C++
- C/C++ can be compiled to wasm
- Shiny can be compiled to wasm, QED
:::
. . .
My response: "Well, _actually_..."
## {background-image="images/iceberg-animation/iceberg-animation.001.jpeg" background-size="contain"}
<!---
## {background-image="images/iceberg-animation/iceberg-animation.002.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.003.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.004.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.005.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.006.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.007.jpeg" background-size="contain"}
## {background-image="images/iceberg-animation/iceberg-animation.008.jpeg" background-size="contain"}
--->
## {background-image="images/iceberg-animation/iceberg-animation.009.jpeg" background-size="contain"}
## Porting is even more difficult than it seems
Some fairly basic computing capabilities are just not available in the browser!
::: incremental
- Cannot launch a process
- Cannot open a network connection
- Cannot perform synchronous I/O (e.g. fetch a web page)
- Cannot handle interrupt signals (e.g. Ctrl-C)
:::
. . .
wasm doesn't change these limitations, it just makes them hurt more.
## {background-image="images/mountain-animation-bw/mountain-animation-bw.001.jpeg" background-size="contain"}
::: sr-only
Dèyè mòn, gen mòn --Haitian proverb
:::
## {background-image="images/mountain-animation-bw/mountain-animation-bw.002.jpeg" background-size="contain"}
::: sr-only
Translation: Beyond mountains, more mountains
:::
## Some people are just built different
- Me: "Stop suggesting we port things to wasm! It's never going to work!"
::: incremental
- [Alon Zakai](https://www.linkedin.com/in/alonzakai/): "👋 I built a compiler toolchain and computing platform that runs on wasm" → emscripten
- [Michael Droettboom](https://www.linkedin.com/in/mdboom/): "👋 I patched NumPy, Matplotlib, and Pandas to run on emscripten" → Pyodide
- [Winston Chang](https://www.linkedin.com/in/winstonchang1/): "👋 I made Shiny run on Pyodide" → Shinylive
- [George Stagg](https://www.linkedin.com/in/george-w-stagg/) and [Lionel Henry](https://twitter.com/_lionelhenry): "👋 We patched R and built a `<canvas>` graphics device to run on emscripten" → WebR
:::
<style>
.profile {
display: none;
}
[data-fragment='0'] .profile[data-with-fragment~='0'],
[data-fragment='1'] .profile[data-with-fragment~='1'],
[data-fragment='2'] .profile[data-with-fragment~='2'],
[data-fragment='3'] .profile[data-with-fragment~='3'],
[data-fragment='4'] .profile[data-with-fragment~='4'],
[data-fragment='5'] .profile[data-with-fragment~='5'] {
display: inline-block;
}
</style>
::: .profiles
![](images/people/alon-zakai.jpg){width=250 alt="Alon Zakai" .profile .border data-with-fragment="0 1 2 3"}
![](images/people/mike-droettboom.jpg){width=250 alt="Michael Droettboom" .profile .border data-with-fragment="1 2 3"}
![](images/people/winston-chang.jpg){width=250 alt="Winston Chang" .profile .border data-with-fragment="2 3"}
![](images/people/george-stagg.jpg){width=250 alt="George Stagg" .profile .border data-with-fragment=3}
![](images/people/lionel-henry.png){width=250 alt="Lionel Henry" .profile .border data-with-fragment=3}
:::
## {.bw-bg background-image="images/mountain-animation/mountain-animation.002.jpeg" background-size="contain"}
## {.bw-to-color-bg background-image="images/mountain-animation/mountain-animation.002.jpeg" background-size="contain" style=""}
## July 2022: Announced Shinylive for Python
. . .
![](images/shinylive-shiny-deployment-model.png){width=60% alt="Traditional Shiny deployment: Python runs on the server, not in the browser"}
. . .
![](images/shinylive-shinylive-deployment-model.png){width=60% alt="Shinylive deployment: Python runs in the browser, not on the server"}
## **Today:** Announcing Shinylive for R!!! {.confetti}
![](images/shinylive-shiny-deployment-model-r.png){width=60% alt="Traditional Shiny deployment: R runs on the server, not in the browser"}
![](images/shinylive-shinylive-deployment-model-r.png){width=60% alt="Shinylive deployment: R runs in the browser, not on the server"}
# How to use Shinylive
## Several different options are available
. . .
:::: {.columns .shinylive-options}
::: {.column width="33%"}
### Convert
:::
::: {.column width="33%"}
### Fiddle
:::
::: {.column width="33%"}
### Include
:::
::::
```{=html}
<style>
.columns.shinylive-options {
display: block;
margin-top: 250px;
}
.columns.shinylive-options .column {
text-align: center;
font-size: 80px;
}
</style>
```
## Option 1: Convert
{shinylive}: Convert local Shiny app to static HTML/CSS/JS/wasm
![](images/convert/convert.001.png){alt="app.R -> shinylive::export() -> index.html & assets"}
## Option 1: Convert
{shinylive}: Convert local Shiny app to static HTML/CSS/JS/wasm
![](images/convert/convert.002.png){alt="app.py -> shinylive export -> index.html & assets"}
## Option 1: Convert
Caveat: For browser security reasons, you can't just double-click index.html to run it locally. You need a real web server. Fortunately, both R and Python have one-liners to start a web server for a directory.
<br>
### Shiny for R:
```r
> shinylive::export(app_dir="myapp", output_dir="site")
> httpuv::runStaticServer("site")
```
<br>
### Shiny for Python:
```python
$ shinylive export myapp site
$ cd site
$ python -m http.server 8000
```
## Option 2: Fiddle
Shinylive.io: Write and share Shiny apps directly in the browser
. . .
:::: columns
::: {.column width="75%"}
![](images/shinylive.png){.border}
:::
::: {.column width="25%"}
**Shiny for Python:**
[https://shinylive.io/py/](https://shinylive.io/py/)
**Shiny for R:**
[https://shinylive.io/r/](https://shinylive.io/r/)
:::
::::
## Option 2: Fiddle
Shinylive.io: Write and share Shiny apps directly in the browser
* View and run examples
* Create your own apps
* Share via URL
* Save to GitHub gist
## Option 3: Include
[Shinylive Quarto extension](https://github.com/quarto-ext/shinylive): Static Shiny apps as Quarto code chunks
````{.r}
```{shinylive-r}
#| standalone: true
ui <- ...
server <- function(input, output, session) {
...
}
shinyApp(ui, server)
```
````
## Option 3: Include
[Shinylive Quarto extension](https://github.com/quarto-ext/shinylive): Static Shiny apps as Quarto code chunks
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
library(shiny)
library(bslib)
theme <- bs_theme(font_scale = 1.5)
# Define UI for app that draws a histogram ----
ui <- page_sidebar(theme = theme,
sidebar = sidebar(open = "open",
numericInput("n", "Sample count", 100),
checkboxInput("pause", "Pause", FALSE),
),
plotOutput("plot", width=1100)
)
server <- function(input, output, session) {
data <- reactive({
input$resample
if (!isTRUE(input$pause)) {
invalidateLater(1000)
}
rnorm(input$n)
})
output$plot <- renderPlot({
hist(data(),
breaks = 40,
xlim = c(-2, 2),
ylim = c(0, 1),
lty = "blank",
xlab = "value",
freq = FALSE,
main = ""
)
x <- seq(from = -2, to = 2, length.out = 500)
y <- dnorm(x)
lines(x, y, lwd=1.5)
lwd <- 5
abline(v=0, col="red", lwd=lwd, lty=2)
abline(v=mean(data()), col="blue", lwd=lwd, lty=1)
legend(legend = c("Normal", "Mean", "Sample mean"),
col = c("black", "red", "blue"),
lty = c(1, 2, 1),
lwd = c(1, lwd, lwd),
x = 1,
y = 0.9
)
}, res=140)
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
```
"I think the students really appreciate seeing it in action, since it’s such a tricky concept to grasp." --Erin Howard, Oregon State University
# Not just cheaper
## {background-image="images/IBM_7010.jpg" background-size="contain"}
::: sr-only
Mainframe computer
:::
::: attribution
Photo credit: [Norsk Teknisk Museum](https://digitaltmuseum.org/011015240045/22-0-ibm-diverse-maskiner), CC BY-SA 4.0
:::
## {background-image="images/DEC_PDP-8-e.jpg" background-size="contain"}
::: sr-only
Minicomputer
:::
::: attribution
Photo credit: Florian Schäffer, CC BY-SA 4.0, via Wikimedia Commons
:::
## {background-image="images/IBM_PC.jpg" background-size="contain"}
::: sr-only
IBM PC
:::
::: attribution
Photo credit: <a href="https://unsplash.com/@bertsz?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">bert b</a> on <a href="https://unsplash.com/photos/Zd6PL6PSW5E?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
:::
## {background-image="images/iphone.jpg" background-size="fill"}
::: sr-only
iPhone
:::
::: attribution
Photo credit: <a href="https://unsplash.com/@cardmapr?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">CardMapr.nl</a> on <a href="https://unsplash.com/photos/tvzykmBf3r0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
:::
## {background-image="images/airtag.jpg" background-size="fill"}
::: sr-only
AirTag on keychain
:::
::: attribution
Photo credit: <a href="https://unsplash.com/@eddiepipocas?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Eddie Pipocas</a> on <a href="https://unsplash.com/photos/wxQavhHf4J4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
:::
## New uses for Shiny
Shiny for Python tutorial is all shinylive
![](images/scenario1.png){.border alt="A tutorial page, featuring text plus an editor pane and Shiny app preview"}
## New uses for Shiny
Shiny for Python function reference docs embed example apps
![](images/scenario2.png){.border alt="A function reference page, showing running Shiny app examples"}
## New uses for Shiny
pkgdown sites for R could use Shiny apps to demonstrate package functionality (hypothetically)
![](images/gt-pkgdown.png){.border alt="The documentation website for the gt package for R" width="700px"}
![](images/gt-pkgdown2.png){.border alt="Another screenshot of gt documentation" width="700px"}
## New uses for Shiny
An R Consortium working group and the FDA are [exploring the possibility](https://rconsortium.github.io/submissions-pilot4/minutes/2023-08-04/) of WebR and Shinylive as a way to include reproducible reports and apps in clinical study reports.
## New uses for Shiny
Your turn!
::: incremental
- Could your next **presentation** be more compelling with some interactivity?
- Could your **lecture notes** lead to deeper understanding if they had interactive examples?
- Would your **blog posts** be more powerful if they featured interactive apps?
- Could you **bewilder** your students with a random game of [Hangman](https://shinylive.io/py/editor/#code=NobwRAdghgtgpmAXGKAHVA6VBPMAaMAYwHsIAXOcpMAMwCdiYACAZwAsBLCbJjmVYnTJMAgujxM6lACZw6EgK4cJUqITIcAbnAA6EPgKGSoEaYz0HBwlmTpcA5nvqMmbE-ZgmaCiIV78rV3dPCD0ACREAOQBxAFkogAUASQBhAGUmAF4mYAByfL0mJgBqAFpy4sKmAB8imqq6+ohGouqGurbmls7M3r6+-NyJQaqyiqra1qqAeQ72qa7Gzpam-rXB4YKusdLKrsmmotmF1rnFs5WetYH8zdzR8t2Ji6Zjw4B6A+Xu+dXr3o2TBG20ee1OJ1eF0+Oh0EKWvyu-0BwKKOzBNReb2W0NhH3h5wW-wBtyBW1RoOeEKxhRxJ3eTBhcLORMygwAunpOdB0AB9JRZJhKLBQexwHk0AA2SmkAAp2kKuKgFGQeWoNKQeQAjZVkUhysD2Yg6fAMsAJAAyIgAmkwIjF4pFjQBKPDyjgYYjKpUqigADxV2jomqgGhg+pCztd5yFnrI3p5foDcmDof1ZH9kYaeidXL0shorDkgZliuVElj3okLDgLBYHFITsQ7QA7oJpALVOotHAMAA1KBSuD653tCVwMgUOgsHn2BQ16vt7KdjTaPsDucy4BsnPnNwQewduBq7trwcy0oARh3jRYxAl2kXkiPXdX-bPADEB9Xr3VpBw4I+y4nm+G6fhK37tO0AACQGrgAojQNBwOoMo-kUMHPiuPZwNo5AlhA3oYIaaFMPmTA8qhTYEnUrZ0NILACsAxpJLkzDaiw2AYMaEjGlacAShKxDNkwABCdiEAA1kwABKxBQNI3GmkkTDmsQ2hMFanqKca0yagAVshwjTHYlAUO2CQMPYdCwDADjGhy1FFLR0gYNWZAytZpiMBghBsMQHCEEOzksE6JGNGOE5yNOs7zgBrnjpu26-EUe72PF7kAAxhXUQpSDAalikoMrVmO6iCAKxp-poiBuCwMoAHxMAAxBFk45mALrJYK7pcNWQh8hwcqOS0CoEcqCZwP6+qtXI2lgOa46TkwAAqxBMNEc61pmw03vxhmCJkxpNemZDcV1jTNmwchwIdKA0JOcGmGdw07tBsE9ghSEoSRGHHquOGmfhhEzXQJFkRRjZdRKAog9Fm0LkRCXZUUHAFqWZAYCDqG8M0Nh2PuGBQCwhAcBwPKCc2ciEETcBMOV6OY4tcjY1wrC2A4hPE6TfLoFTNNUSsKwSoTvOmEDyqM5FdCoRgCi89LoXnXUsMzvDcVuTKErIy0qM48DTMK7L8vYxAxDCKzzmI+5TpG6gzOQztgtsAKqVW6hSsrM7xTZBeHstK7GtsNrgtMKg+PuUHfuNGHXDucajylCOjsrDHeFjhAMp2nEiSpGkivJzrBbO-V2Tp5nUTZ5EyTpE6l4CyHDdFLlcD5doA3FXtZV0BVYBVTVRMNc1IPtZ1Bch6NfUqkVUeN91WD6la0wAKoqdMaRwQAhNts874WpW6t32RHSdz277Pl3XT3UD3XIj0KWAM8rMHZ8MqEb8h1BFbKl1GGmHIGCJi6mREIlE-ZSDIAoOgGcs4OmrmkYArtRTWySgXH+X9TrDV-rIOgADJoYJDmRE6oCC7CWyJbJB7sC6xhdsEEwMpUBsGstWAUzYJAqxirWACMMDZw1ii5Ch+cd662ob0JgzZ65n2bq3Qqg0Sr7UPqaPutVB4tQNiPKML8569TkFPQaj8RrulQAvZeTAADq0xIhbw6hozRdQ5Fdx7sdDM+B9EXSulIK+N86B32NPo5+RR+LVgkbvIUctpAhjFImaaBtFKaHXLTI+D8OpgPHJAjOsZXpvzQKgAUYgjHZIGlWIscgdxgAAL54HANAeA1BUohG8L4LAuACAkHIKZagZE6l0IYUwuAbCeGqz4Q7RoIjTS+PODQemONjD7iHGXHpNNQrBKEQWBZ1ZgAcDZNMpiYBTQ8TAPcE08c5pcSOS4sAbJlkv2od7UOjCaYbIcpo-ius1lwEebbe20z2Fq2kFcs+NzshvMefowJcB-m70BaaHk4zGjgLSTKDJehylsiAA) in the middle of their homework?
:::
## Recap
* **Convert**: Use the {shinylive} package to convert app.R/app.py to static HTML
* **Fiddle**: Visit [https://shinylive.io/py/](https://shinylive.io/py/) or [https://shinylive.io/r/](https://shinylive.io/r/) to start writing and running Shiny apps in the browser
* **Include**: Use quarto-shinylive to embed Shiny apps in Quarto documents, presentations, and websites
## Limitations
- Slower startup time, larger download size
- Especially bad for R right now, fixes coming very soon
- Not all of CRAN or PyPI is available
- Some functions just don't work (R: `system()` and `{future}`, Python: `multiprocessing`)
- Can't connect directly to databases (but API calls may work)
- Code/data is fully visible to users
## Thank you!
Copy of these slides, and links to next steps:
[https://bit.ly/shinylive-2023](https://bit.ly/shinylive-2023)
![](images/qrcode.png){alt="QR code to the same link as above" style="position: absolute; right: 0;"}
```{=html}
<style>
.border {
border: 2px solid #ccc;
border-radius: 5px;
padding: 5px;
}
.attribution {
position: absolute;
bottom: 0;
right: 0;
width: auto;
height: auto;
font-size: 1.25rem;
background-color: white;
padding: 5px;
border: 2px solid #ccc;
}
.attribution p {
margin: 0;
}
.sr-only {
text-indent: -999999px;
}
pre code.sourceCode {
font-size: 1.7em;
}
[data-engine] div.shinylive-wrapper {
margin: 0;
}
.reveal .shinylive-wrapper iframe {
max-width: unset;
max-height: unset;
}
</style>
```
<script src="js/tsparticles.confetti.bundle.min.js"></script>
<script>
setTimeout(() => {
Reveal.on("slidechanged", event => {
if (event.currentSlide.matches(".bw-bg")) {
event.currentSlide.slideBackgroundElement.style.filter = "grayscale(100%)";
}
if (event.currentSlide.matches(".bw-to-color-bg")) {
event.currentSlide.slideBackgroundElement.style.filter = "grayscale(100%)";
event.currentSlide.slideBackgroundElement.style.transition = "filter 500ms ease-in-out";
setTimeout(() => {
event.currentSlide.slideBackgroundElement.style.filter = null;
}, 100);
}
if (event.currentSlide.matches(".confetti")) {
const duration = 3 * 1000,
animationEnd = Date.now() + duration,
defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 };
function randomInRange(min, max) {
return Math.random() * (max - min) + min;
}
const interval = setInterval(function() {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
return clearInterval(interval);
}
const particleCount = 50 * (timeLeft / duration);
// since particles fall down, start a bit higher than random
confetti(
Object.assign({}, defaults, {
particleCount,
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
})
);
confetti(
Object.assign({}, defaults, {
particleCount,
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
})
);
}, 250);
}
});
}, 2000);
</script>