

{"id":13834,"date":"2023-03-18T09:19:56","date_gmt":"2023-03-18T14:19:56","guid":{"rendered":"https:\/\/rud.is\/b\/?p=13834"},"modified":"2023-03-18T14:00:21","modified_gmt":"2023-03-18T19:00:21","slug":"the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots","status":"publish","type":"post","link":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/","title":{"rendered":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots"},"content":{"rendered":"<p>I have graphics working in Vanilla JS WebR, now, and I&#8217;ll cover the path to that in two parts.<\/p>\n<p>The intent was to jump straight into ggplot2-land, but, as you saw in my previous post, WASM&#8217;d ggplot2 is a bear. And, I really didn&#8217;t grok what the WebR site docs were saying about how to deal with the special WebR <code>canvas()<\/code> device until I actually tried to work with it and failed miserably.<\/p>\n<p>You will need to have gotten caught up on the previous WebR blog posts and <a href=\"https:\/\/github.com\/hrbrmstr\/webr-experiments\">experiments<\/a> as I&#8217;m just covering some of the gnarly bits.<\/p>\n<h3>Not Your Parents&#8217; <code>evalR\u2026()<\/code><\/h3>\n<p>If you&#8217;ve either been playing a bit with WebR or peeked under the covers of what others are doing, you&#8217;ve seen the <code>evalR\u2026()<\/code> family of functions which evaluate supplied R code and optionally return a result. Despite reading the <a href=\"https:\/\/docs.r-wasm.org\/webr\/v0.1.0\/communication.html#plotting-with-the-webr-html-canvas\">WebR docs<\/a> on &#8220;canvas&#8221;, daft me tried to simply use one of those &#8220;eval&#8221; functions, to no avail.<\/p>\n<p>The solution involves:<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.r-wasm.org\/webr\/v0.1.0\/objects.html#shelter\">shelters<\/a><\/li>\n<li><a href=\"https:\/\/docs.r-wasm.org\/webr\/v0.1.0\/evaluating.html#evaluating-r-code-and-capturing-output-with-capturer\">captureR<\/a><\/li>\n<li>dealing with a <em>ton<\/em> of &#8220;messages&#8221;<\/li>\n<\/ul>\n<p>I&#8217;m going to block quote a key <code>captureR<\/code> since it explains &#8220;why&#8221; pretty well. Hit me up anywhere you like if you desire more info.<\/p>\n<blockquote><p>\n  Unlike evalR() which only returns one R object, captureR() returns a variable number of objects when R conditions are captured. Since this makes memory management of individual objects unwieldy, captureR() requires the shelter approach to memory management, where all the sheltered objects are destroyed at once.\n<\/p><\/blockquote>\n<p>Let&#8217;s work through the &#8220;plottR&#8221; function I made to avoid repeating code to just get images out of R. It takes, as input:<\/p>\n<ul>\n<li>an initialized WebR context<\/li>\n<li>code that will produce something on a graphics device<\/li>\n<li>dimensions of the image<\/li>\n<li>the HTML <code>&lt;canvas&gt;<\/code> <code>id<\/code> to shove the image data to (we&#8217;ll explain this after the code block)<\/li>\n<\/ul>\n<pre><code class=\"language-js\">async function plottR(webR, plot_code = \"plot(mtcars, col='blue')\",\n                        width = 400, height = 400, \n                            id = \"base-canvas\") {\n\n  const webRCodeShelter = await new webR.Shelter();\n\n  await webR.evalRVoid(`canvas(width=${width}, height=${height})`);\n\n  const result = await webRCodeShelter.captureR(`${plot_code}`, {\n    withAutoprint: true,\n    captureStreams: true,\n    captureConditions: false,\n    env: webR.objs.emptyEnv,\n  });\n\n  await webR.evalRVoid(\"dev.off()\");\n\n  const msgs = await webR.flush();\n\n  const canvas = document.getElementById(id)\n  canvas.setAttribute(\"width\", 2 * width);\n  canvas.setAttribute(\"height\", 2 * height);\n\n  msgs.forEach(msg =&gt; {\n    if (msg.type === \"canvasExec\") Function(`this.getContext(\"2d\").${msg.data}`).bind(canvas)()\n  });\n\n}\n<\/code><\/pre>\n<p>You 100% need to read up on the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/canvas\">HTML canvas element<\/a> if you&#8217;re going to wield WebR yourself vs use Quarto, Shiny, Jupyter-lite, or anything else clever folks come up with. The output of your plots is going to be a series of HTML canvas instructions to do things like &#8220;move here&#8221;, &#8220;switch to this color&#8221;, &#8220;draw an ellipse&#8221;, etc. I will be linking to a full example of the canvas instructions output towards the end.<\/p>\n<p>Now, let&#8217;s work through the function&#8217;s innards.<\/p>\n<pre><code class=\"language-js\">const webRCodeShelter = await new webR.Shelter();\n<\/code><\/pre>\n<p>gives us a temporary place to execute R code, knowing all the memory consumed will go away after we&#8217;re immediately done with it. Unlike the baked-in &#8220;global&#8221; shelter, this one is super ephemeral.<\/p>\n<pre><code class=\"language-js\">await webR.evalRVoid(`canvas(width=${width}, height=${height})`);\n<\/code><\/pre>\n<p>This is just like a call to <code>png(\u2026)<\/code>, <code>svglite(\u2026)<\/code>, <code>pdf(\u2026)<\/code>, etc. Check out <a href=\"https:\/\/github.com\/coolbutuseless\">coolbutuseless&#8217; repo<\/a> for tons of great examples of alternate graphics devices. I have a half-finished one for omnigraffle. They aren&#8217;t &#8220;hard&#8221; to write, but I think they are very tedious to crank through.<\/p>\n<pre><code class=\"language-js\">const result = await webRCodeShelter.captureR(`${plot_code}`, {\n  withAutoprint: true,\n  captureStreams: true,\n  captureConditions: false,\n  env: webR.objs.emptyEnv,\n});\n<\/code><\/pre>\n<p>is different from what you&#8217;re used to. The <code>captureR<\/code> function will evaluate the given code, and takes some more options, <a href=\"https:\/\/docs.r-wasm.org\/webr\/v0.1.0\/api\/js\/interfaces\/WebRChan.EvalROptions.html\">described in the docs<\/a>. TL;DR: we&#8217;re asking the evaluator to give us back pretty much what&#8217;d we see in the R console: all console messages and output streams, plus it does the familiar &#8220;R object autoprint&#8221; that you get for free when you fire up an R console.<\/p>\n<p>So, we&#8217;ve sent our plot code into the abyss, and \u2014 since this is 100% like &#8220;normal&#8221; graphics devices \u2014 we also need to do the <code>dev.off<\/code> dance:<\/p>\n<pre><code class=\"language-js\">await webR.evalRVoid(\"dev.off()\");\n<\/code><\/pre>\n<p>This will cause the rendering to happen.<\/p>\n<p>Right now, where you can&#8217;t see it, is the digital manifestation of your wonderful plot. That&#8217;s great, but we&#8217;d like to see it!<\/p>\n<pre><code class=\"language-js\">const msgs = await webR.flush();\n<\/code><\/pre>\n<p>will tell it to get on with it and make sure everything that needs to be done is done. If you&#8217;re not familiar with async\/await yet, you really need to dig into that to survive in DIY WebR land.<\/p>\n<pre><code class=\"language-js\">const canvas = document.getElementById(id)\ncanvas.setAttribute(\"width\", 2 * width);   \/\/ i still need to read \"why 2x\"\ncanvas.setAttribute(\"height\", 2 * height);\n\nmsgs.forEach(msg =&gt; {\n  if (msg.type === \"canvasExec\") Function(`this.getContext(\"2d\").${msg.data}`).bind(canvas)()\n});\n<\/code><\/pre>\n<p>finds our HTML canvas element and then throws messages at it; <em>alot<\/em> of messages. To see the generated code for the 3D perspective plot example, <a href=\"https:\/\/gist.github.com\/hrbrmstr\/282bbf66f648568520c135d574ca1e6a\">head to this gist<\/a> where I&#8217;ve pasted all ~10K instructions.<\/p>\n<p>To make said <code>persp<\/code> plot, it&#8217;s just a simple call, now:<\/p>\n<pre><code class=\"language-js\">await plottR(webR, `basetheme(\"dark\"); persp(x, y, z, theta=-45)`)\n<\/code><\/pre>\n<p>I used the default <code>id<\/code> for the canvas in the online example.<\/p>\n<h3>&#8220;How Did You Use The basetheme Package? It&#8217;s Not In The WASM R Repo?&#8221;<\/h3>\n<p>I yanked the four R source code files from <a href=\"https:\/\/cran.r-project.org\/web\/packages\/basetheme\/index.html\">the package<\/a> and just <code>source<\/code>&#8216;d them into the WebR environment:<\/p>\n<pre><code class=\"language-js\">const baseThemePackage = [ \"basetheme.R\", \"coltools.R\", \"themes.R\", \"utils.R\" ];\n\n\/\/ load up the source from the basetheme pkg\nfor (const rSource of baseThemePackage) {\n  console.log(`Sourcing: ${rSource}\u2026`)\n  await globalThis.webR.evalRVoid(`source(\"https:\/\/rud.is\/w\/ggwebr\/r\/${rSource}\")`)\n}\n<\/code><\/pre>\n<h3>10K+ Lines Is Alot Canvas Code\u2026<\/h3>\n<p>Yep! But, that&#8217;s how the HTML canvas element works and it&#8217;s shockingly fast, as you&#8217;ve seen via the demo.<\/p>\n<h3>FIN<\/h3>\n<p>We&#8217;ll cover a bit more in part 2 when we see how to get ggplot2 working, which will include a WebR version of {hrbrthemes}! I also want to thank James Balamuta for the Quarto WebR project which helped me out quite a bit in figuring this new tech out.<\/p>\n<p>Before I let you go, I wanted to note that in those &#8220;messages&#8221; (the ones we pulled <code>canvasExec<\/code> call out of), there are message types that are <em>not<\/em> <code>canvasExec<\/code> (hence our need to filter them).<\/p>\n<p>I thought you might want to know what they are, so I extracted the JSON, and ran it through some {dplyr}:<\/p>\n<pre><code class=\"language-r\">msgs |&gt; \n  filter(\n    type != \"canvasExec\"\n  ) |&gt; \n  pull(data) |&gt; \n  writeLines()\nR version 4.1.3 (2022-03-10) -- \"One Push-Up\"\nCopyright (C) 2022 The R Foundation for Statistical Computing\nPlatform: wasm32-unknown-emscripten (32-bit)\n\nR is free software and comes with ABSOLUTELY NO WARRANTY.\nYou are welcome to redistribute it under certain conditions.\nType 'license()' or 'licence()' for distribution details.\n\nR is a collaborative project with many contributors.\nType 'contributors()' for more information and\n'citation()' on how to cite R or R packages in publications.\n\nType 'demo()' for some demos, 'help()' for on-line help, or\n'help.start()' for an HTML browser interface to help.\nType 'q()' to quit R.\n\n&gt; \n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I have graphics working in Vanilla JS WebR, now, and I&#8217;ll cover the path to that in two parts. The intent was to jump straight into ggplot2-land, but, as you saw in my previous post, WASM&#8217;d ggplot2 is a bear. And, I really didn&#8217;t grok what the WebR site docs were saying about how to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":13836,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[753,91,867],"tags":[],"class_list":["post-13834","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ggplot","category-r","category-webr"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is\" \/>\n<meta property=\"og:description\" content=\"I have graphics working in Vanilla JS WebR, now, and I&#8217;ll cover the path to that in two parts. The intent was to jump straight into ggplot2-land, but, as you saw in my previous post, WASM&#8217;d ggplot2 is a bear. And, I really didn&#8217;t grok what the WebR site docs were saying about how to [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/\" \/>\n<meta property=\"og:site_name\" content=\"rud.is\" \/>\n<meta property=\"article:published_time\" content=\"2023-03-18T14:19:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-03-18T19:00:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1\" \/>\n\t<meta property=\"og:image:width\" content=\"1344\" \/>\n\t<meta property=\"og:image:height\" content=\"1656\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"hrbrmstr\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"hrbrmstr\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/\"},\"author\":{\"name\":\"hrbrmstr\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"headline\":\"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots\",\"datePublished\":\"2023-03-18T14:19:56+00:00\",\"dateModified\":\"2023-03-18T19:00:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/\"},\"wordCount\":875,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"image\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/03\\\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1\",\"articleSection\":[\"ggplot\",\"R\",\"webr\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/\",\"name\":\"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/03\\\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1\",\"datePublished\":\"2023-03-18T14:19:56+00:00\",\"dateModified\":\"2023-03-18T19:00:21+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#primaryimage\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/03\\\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/03\\\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1\",\"width\":1344,\"height\":1656,\"caption\":\"WebR plot example with a faceted scatterplot\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2023\\\/03\\\/18\\\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/rud.is\\\/b\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/\",\"name\":\"rud.is\",\"description\":\"&quot;In God we trust. All others must bring data&quot;\",\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/rud.is\\\/b\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\",\"name\":\"hrbrmstr\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"width\":460,\"height\":460,\"caption\":\"hrbrmstr\"},\"logo\":{\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\"},\"description\":\"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7\",\"sameAs\":[\"http:\\\/\\\/rud.is\"],\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/author\\\/hrbrmstr\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/","og_locale":"en_US","og_type":"article","og_title":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is","og_description":"I have graphics working in Vanilla JS WebR, now, and I&#8217;ll cover the path to that in two parts. The intent was to jump straight into ggplot2-land, but, as you saw in my previous post, WASM&#8217;d ggplot2 is a bear. And, I really didn&#8217;t grok what the WebR site docs were saying about how to [&hellip;]","og_url":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/","og_site_name":"rud.is","article_published_time":"2023-03-18T14:19:56+00:00","article_modified_time":"2023-03-18T19:00:21+00:00","og_image":[{"width":1344,"height":1656,"url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","type":"image\/png"}],"author":"hrbrmstr","twitter_card":"summary_large_image","twitter_misc":{"Written by":"hrbrmstr","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#article","isPartOf":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/"},"author":{"name":"hrbrmstr","@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"headline":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots","datePublished":"2023-03-18T14:19:56+00:00","dateModified":"2023-03-18T19:00:21+00:00","mainEntityOfPage":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/"},"wordCount":875,"commentCount":2,"publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"image":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","articleSection":["ggplot","R","webr"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/","url":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/","name":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots - rud.is","isPartOf":{"@id":"https:\/\/rud.is\/b\/#website"},"primaryImageOfPage":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#primaryimage"},"image":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#primaryimage"},"thumbnailUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","datePublished":"2023-03-18T14:19:56+00:00","dateModified":"2023-03-18T19:00:21+00:00","breadcrumb":{"@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#primaryimage","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","width":1344,"height":1656,"caption":"WebR plot example with a faceted scatterplot"},{"@type":"BreadcrumbList","@id":"https:\/\/rud.is\/b\/2023\/03\/18\/the-road-to-ggplot2-in-webr-part-1-the-road-is-paved-with-good-base-r-plots\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/rud.is\/b\/"},{"@type":"ListItem","position":2,"name":"The Road To ggplot2 In WebR, Part 1: The Road Is Paved With Good Base R Plots"}]},{"@type":"WebSite","@id":"https:\/\/rud.is\/b\/#website","url":"https:\/\/rud.is\/b\/","name":"rud.is","description":"&quot;In God we trust. All others must bring data&quot;","publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/rud.is\/b\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886","name":"hrbrmstr","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","width":460,"height":460,"caption":"hrbrmstr"},"logo":{"@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1"},"description":"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7","sameAs":["http:\/\/rud.is"],"url":"https:\/\/rud.is\/b\/author\/hrbrmstr\/"}]}},"jetpack_featured_media_url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-18-at-10.10.23.png?fit=1344%2C1656&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/p23idr-3B8","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":13824,"url":"https:\/\/rud.is\/b\/2023\/03\/18\/webr-wasm-r-package-load-library-benchmarking-rabbit-hole\/","url_meta":{"origin":13834,"position":0},"title":"WebR WASM R Package Load\/Library Benchmarking Rabbit Hole","author":"hrbrmstr","date":"2023-03-18","format":false,"excerpt":"I have a post coming on using base and {ggplot2} plots in VanillaJS WebR, but after posting some bits on social media regarding how slow {ggplot2} is to deal with, I had some \"performance\"-related inquiries, which led me down a rabbit hole that I'm, now, dragging y'all down into as\u2026","rel":"","context":"In &quot;Javascript&quot;","block_context":{"text":"Javascript","link":"https:\/\/rud.is\/b\/category\/javascript\/"},"img":{"alt_text":"Histogram of WASM R package load times. Most load in well under 1s","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/pkgload.png?fit=1200%2C869&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/pkgload.png?fit=1200%2C869&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/pkgload.png?fit=1200%2C869&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/pkgload.png?fit=1200%2C869&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/pkgload.png?fit=1200%2C869&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":13927,"url":"https:\/\/rud.is\/b\/2023\/04\/10\/introducing-webrider-the-webr-ide-ish-repl-you-didnt-know-you-needed\/","url_meta":{"origin":13834,"position":1},"title":"Introducing WebRIDEr: The WebR &#8220;IDE&#8221;-ish REPL You Didn&#8217;t Know You Needed","author":"hrbrmstr","date":"2023-04-10","format":false,"excerpt":"The official example WebR REPL is definitely cool and useful to get the feel for WebR. But, it is far from an ideal way to deal with it interactively, even as just a REPL. As y'all know, I've been conducing numerous experiments with WebR and various web technologies. I started\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"ide-like view","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/04\/preview.png?fit=1200%2C754&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/04\/preview.png?fit=1200%2C754&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/04\/preview.png?fit=1200%2C754&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/04\/preview.png?fit=1200%2C754&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/04\/preview.png?fit=1200%2C754&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":13885,"url":"https:\/\/rud.is\/b\/2023\/03\/26\/%f0%9f%a7%aa-lit-webr-observable-plot-linking-lits-lightweight-web-components-and-webr-for-vanilla-js-reactivity-js-datavis\/","url_meta":{"origin":13834,"position":2},"title":"? Lit + WebR + Observable Plot: Linking Lit&#8217;s Lightweight Web Components And WebR For Vanilla JS Reactivity &#038; JS DataVis","author":"hrbrmstr","date":"2023-03-26","format":false,"excerpt":"See it live before reading! The previous post brought lit-webr, to introduce Lit and basic reactivity. Today, is more of the same, but we bring the OG Shiny demo plot into the modern age by using Observbable Plot to make the charts. We're still pulling data from R, but we're\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"popup menu + barplot","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/preview.png?fit=1200%2C1174&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/preview.png?fit=1200%2C1174&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/preview.png?fit=1200%2C1174&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/preview.png?fit=1200%2C1174&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/preview.png?fit=1200%2C1174&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":13789,"url":"https:\/\/rud.is\/b\/2023\/03\/12\/almost-bare-bones-webr-starter-app\/","url_meta":{"origin":13834,"position":3},"title":"Almost Bare Bones WebR Starter App","author":"hrbrmstr","date":"2023-03-12","format":false,"excerpt":"Let's walk through how to set up a ~minimal HTML\/JS\/CSS + WebR-powered \"app\" on a server you own. This will be vanilla JS (i.e. no React\/Vue\/npm\/bundler) you can hack on at-will. TL;DR: You can find the source to the app and track changes to it over on GitHub if you\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"screenshot of the example webr app showing a portion of mtcars","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-12-at-10.24.18.png?fit=1200%2C772&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-12-at-10.24.18.png?fit=1200%2C772&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-12-at-10.24.18.png?fit=1200%2C772&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-12-at-10.24.18.png?fit=1200%2C772&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/03\/Screenshot-2023-03-12-at-10.24.18.png?fit=1200%2C772&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":13845,"url":"https:\/\/rud.is\/b\/2023\/03\/20\/webr-filesystem-and-reefr\/","url_meta":{"origin":13834,"position":4},"title":"WebR Filesystem Machinations &#038; ReefR","author":"hrbrmstr","date":"2023-03-20","format":false,"excerpt":"It's difficult to believe it has only been a couple of weeks since WebR has been around. But that might just be my perception. The spike protein invasion has significantly increased sedentary time, and that has enabled me to focus on this new toy to keep my attention focused on\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":13773,"url":"https:\/\/rud.is\/b\/2023\/03\/09\/webr-is-here\/","url_meta":{"origin":13834,"position":5},"title":"WebR IS HERE!","author":"hrbrmstr","date":"2023-03-09","format":false,"excerpt":"WebR 0.1.0 was released! I had been git-stalking George (the absolute genius who we all must thank for this) for a while and noticed the GH org and repos being updated earlier this week, So, I was already pretty excited. It dropped today, and you can hit that link for\u2026","rel":"","context":"In &quot;d3&quot;","block_context":{"text":"d3","link":"https:\/\/rud.is\/b\/category\/d3\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/13834","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/comments?post=13834"}],"version-history":[{"count":0,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/13834\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media\/13836"}],"wp:attachment":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media?parent=13834"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/categories?post=13834"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/tags?post=13834"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}