<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - JupyterLite</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/tag-jupyterlite.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2026-07-10T05:49:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>JupyterLite 0.8 is released! 🎉</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/" rel="alternate"/><published>2026-07-10T05:01:00+00:00</published><updated>2026-07-10T05:49:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2026-07-10:/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/</id><summary type="html">&lt;p&gt;JupyterLite is a Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://jupyterlite.readthedocs.io/"&gt;JupyterLite&lt;/a&gt; is a Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy as JupyterLite sites are just static websites.&lt;/p&gt;
&lt;p&gt;The new 0.8 release includes a number of new features, bug fixes, and enhancements. This release also brings significant improvements to the user experience and new customization options for JupyterLite deployments.&lt;/p&gt;
&lt;h2 id="sharing-a-repl"&gt;Sharing a REPL&lt;/h2&gt;
&lt;p&gt;When the &lt;a href="https://jupyterlite.readthedocs.io/en/stable/quickstart/embed-repl.html#enable-the-toolbar"&gt;REPL toolbar&lt;/a&gt; is enabled, it now includes a &lt;strong&gt;Copy Shareable Link&lt;/strong&gt; button. Clicking it captures the current state of the REPL, the prompt content, the selected kernel and theme, and any non-default options, into the page URL, and copies the resulting link to the clipboard. A notification confirms the link was copied, making it easy to share a ready-to-run REPL with others.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/001-1_nj2zTCdB_5ZOP9UpQ1q_yg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="refreshed-loading-screen"&gt;Refreshed Loading Screen&lt;/h2&gt;
&lt;p&gt;The loading indicator shown while JupyterLite starts up has been redesigned to match the JupyterLab splash screen to make the transition from the initial loading screen to the fully loaded application feel more seamless.&lt;/p&gt;
&lt;p&gt;The loading indicator remains configurable, and can be enabled or disabled for specific applications. See the &lt;a href="https://jupyterlite.readthedocs.io/en/stable/howto/configure/loading_indicator.html"&gt;loading indicator guide&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/002-1_EFPxwNDTRN3933qULcA3Rg.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="file-system-reliability-improvements"&gt;File System Reliability Improvements&lt;/h2&gt;
&lt;p&gt;JupyterLite 0.8 includes many fixes to the in-browser file system used to store your notebooks and files. Creating files and directories is now faster and more reliable, saving a file to a directory that does not exist is now rejected with a clear error rather than failing silently, and files are no longer opened twice on startup when using query parameters. These changes make working with files in JupyterLite smoother and more dependable.&lt;/p&gt;
&lt;h2 id="jupyterlab-46-and-notebook-76"&gt;JupyterLab 4.6 and Notebook 7.6&lt;/h2&gt;
&lt;p&gt;JupyterLite 0.8 is built on top of JupyterLab 4.6 and Notebook 7.6, and brings many of the respective improvements and bug fixes. Check out the &lt;a href="/posts/2026/jupyterlab-4-6-and-notebook-7-6-are-out/"&gt;announcement post&lt;/a&gt; to learn more about these new releases.&lt;/p&gt;
&lt;p&gt;This JupyterLite release includes 9 new features and enhancements, 11 bug fixes, and 5 documentation improvements. 13 people contributed to this release. Across 85 maintenance tasks and dependency updates, we kept JupyterLite up to date with the latest JupyterLab and Notebook releases.&lt;/p&gt;
&lt;h2 id="a-terminal-in-the-browser"&gt;A Terminal in the Browser&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyterlite/terminal"&gt;JupyterLite Terminal&lt;/a&gt; adds a fully in-browser command-line terminal to JupyterLite, powered by the &lt;a href="https://github.com/jupyterlite/cockle"&gt;cockle&lt;/a&gt; shell. Commands run as WebAssembly directly in the browser, with no server involved. Version 1.5 adds compatibility with JupyterLite 0.8, and recent releases include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A growing set of familiar command-line tools compiled to WebAssembly, including &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;tree&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, and the &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;nano&lt;/code&gt; editors.&lt;/li&gt;
&lt;li&gt;Tab completion for commands, arguments, and file names, with a confirmation prompt before listing a very large number of matches.&lt;/li&gt;
&lt;li&gt;New JupyterLab commands for running shell commands programmatically and capturing their output, without opening a terminal panel.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Running terminal commands in the browser" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/003-0_Zdo8FbjAiAGkh3IH.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Running terminal commands in the browser&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="more-kernels-with-jupyterlite-xeus"&gt;More Kernels with jupyterlite-xeus&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jupyterlite-xeus.readthedocs.io/"&gt;jupyterlite-xeus&lt;/a&gt; builds JupyterLite deployments around &lt;a href="https://xeus.readthedocs.io/"&gt;Xeus&lt;/a&gt;-based kernels and lets you pre-install packages from &lt;a href="https://conda-forge.org/"&gt;conda-forge&lt;/a&gt; and emscripten-forge straight into the browser environment through an &lt;code&gt;environment.yml&lt;/code&gt; file. Its recent releases (the 4.5 series and the new 5.0) are compatible with JupyterLite 0.8 and add a number of improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A growing list of supported kernels, including &lt;code&gt;xeus-python&lt;/code&gt;, &lt;code&gt;xeus-r&lt;/code&gt;, &lt;code&gt;xeus-cpp&lt;/code&gt;, &lt;code&gt;xeus-lua&lt;/code&gt;, &lt;code&gt;xeus-nelson&lt;/code&gt;, &lt;code&gt;xeus-octave&lt;/code&gt;, and &lt;code&gt;xeus-javascript&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Installing packages at runtime now feels more natural: you can run &lt;code&gt;pip install&lt;/code&gt;or &lt;code&gt;conda install&lt;/code&gt;directly in a cell, with the leading &lt;code&gt;%&lt;/code&gt; or &lt;code&gt;!&lt;/code&gt; now optional.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn more about pre-installing packages, mounting files, and the available kernels, see the &lt;a href="https://jupyterlite-xeus.readthedocs.io/"&gt;jupyterlite-xeus documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="python-314-with-the-pyodide-kernel"&gt;Python 3.14 with the Pyodide Kernel&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyterlite/pyodide-kernel"&gt;Pyodide kernel&lt;/a&gt; has been updated to version 0.8.0. It bundles the latest &lt;a href="https://pyodide.org/"&gt;Pyodide&lt;/a&gt; release, which upgrades the in-browser Python interpreter to &lt;strong&gt;Python 3.14&lt;/strong&gt;. Notebooks and consoles running on the Pyodide kernel now use the newest Python version, including recent standard library additions such as the new &lt;code&gt;compression.zstd&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;For deployments that pre-compute the set of packages to ship offline, the kernel’s lock add-on can now build a customized &lt;code&gt;pyodide-lock.json&lt;/code&gt; from your own requirements — including reading dependencies from a &lt;code&gt;requirements.txt&lt;/code&gt; file or a &lt;code&gt;pyproject.toml&lt;/code&gt; dependency group — giving site authors finer control over which packages are available without a network connection.&lt;/p&gt;
&lt;p&gt;See the &lt;a href="https://github.com/jupyterlite/pyodide-kernel/releases/tag/v0.8.0"&gt;pyodide-kernel 0.8.0 release notes&lt;/a&gt; for the full list of changes.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Pyodide 314 in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/004-1_kMihemTy3YNeaStnK6bH1A.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Pyodide 314 in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="javascript-kernel-improvements"&gt;JavaScript Kernel Improvements&lt;/h2&gt;
&lt;p&gt;The in-browser &lt;a href="https://github.com/jupyterlite/javascript-kernel"&gt;JavaScript kernel&lt;/a&gt; has gained several improvements in its 0.4 development line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new iframe-based execution mode with better support for libraries that need direct access to the DOM, such as &lt;a href="https://p5js.org/"&gt;p5.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Initial support for Jupyter Widgets, through Comm channels and a set of base widgets.&lt;/li&gt;
&lt;li&gt;Clearer error messages and tracebacks, including a fix for errors that previously rendered as blank in Firefox.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Widgets and dynamic imports with the JavaScript kernel" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/005-1_uhOWj2tSrsSP_maQk3vWaA.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Widgets and dynamic imports with the JavaScript kernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="try-it-in-your-browser"&gt;Try it in your browser&lt;/h2&gt;
&lt;p&gt;If you would like to try the new release in your browser, click on the following link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyter.org/try-jupyter/"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/006-0_WKrNZ-b6NXSbf5UG.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href="https://x.com/TechAtBloomberg"&gt;Bloomberg&lt;/a&gt; for funding the work on JupyterLite at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;, and for their ongoing support for JupyterLite in general.&lt;/p&gt;
&lt;p&gt;Many thanks to all the contributors to JupyterLite. The list below is based on GitHub usernames for the 0.8 release across the different projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New contributors: @Darshan808, @MMesch, @MUFFANUJ, @wata727&lt;/li&gt;
&lt;li&gt;Returning contributors: @DerThorsten, @agriyakhetarpal, @bollwyvl, @ianthomas23, @jasongrout, @jtpio, @martinRenou, @trungleduc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you to the extended group of contributors in Jupyter Frontends and related project in the Jupyter ecosystem 💛&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-0-8-is-released/images/007-0_AJW_fYChAaoUhRbD.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLite"/><category term="releases"/><category term="WebAssembly"/></entry><entry><title>A User’s Journey with Plugin Playground: From First Idea to Installable JupyterLab Extension.</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/" rel="alternate"/><published>2026-05-28T17:45:00+00:00</published><updated>2026-05-28T18:07:00+00:00</updated><author><name>Anuj Singh</name></author><id>tag:jasongrout.github.io,2026-05-28:/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/</id><summary type="html">&lt;p&gt;We are excited to announce the 1.0 release of JupyterLab Plugin Playground, allowing you to seamlessly experiment with the creation of…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are excited to announce the 1.0 release of JupyterLab Plugin Playground, allowing you to seamlessly experiment with the creation of Jupyter Notebook and JupyterLab plugins to add any functionality you may desire. To install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install jupyterlab-plugin-playground
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or use it from Binder or JupyterLite without installing anything by clicking one of these links: &lt;a href="https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=lab"&gt;Binder (Lab)&lt;/a&gt;, &lt;a href="https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=tree"&gt;Binder (Notebook v7)&lt;/a&gt;, &lt;a href="https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/lab/"&gt;JupyterLite (Lab)&lt;/a&gt;, &lt;a href="https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/tree/"&gt;JupyterLite (Notebook v7)&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="motivation"&gt;&lt;strong&gt;Motivation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Building a JupyterLab extension has traditionally required setting up a complex local environment: installing dependencies from disjoint ecosystems (Node.js and Python), configuring tooling, and hunting down the right documentation. The goal of the Playground is to eliminate that setup overhead and streamline extension development. By bringing the runtime, documentation, and the context required for extension development into a single browser tab, you can go from idea to working extension without any local setup required.&lt;/p&gt;
&lt;h2 id="journey-at-a-glance"&gt;&lt;strong&gt;Journey At a Glance&lt;/strong&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start with a tiny extension idea that is easy to verify visually.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Choose a build path: &lt;strong&gt;Manual&lt;/strong&gt; or &lt;strong&gt;AI-assisted&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Load&lt;/strong&gt; and iterate inside JupyterLab until behavior is stable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build a second extension to prove the workflow is repeatable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Share plugin files/packages for product and engineering review.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export as a &lt;strong&gt;wheel(.whl)&lt;/strong&gt; and validate installation in a clean Binder runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export as a &lt;strong&gt;zip&lt;/strong&gt; and move the scaffold into a normal GitHub repository.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="10-pick-your-starting-path"&gt;&lt;strong&gt;1.0: Pick Your Starting Path&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;At the beginning, choose the path that matches your confidence level and speed needs. If this is your first time with Plugin Playground, you can optionally run &lt;code&gt;Take the Tour&lt;/code&gt; from the Launcher or Command Palette for quick orientation before starting.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Start from File&lt;/code&gt; If you already know the APIs you need and want direct control.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Build with AI&lt;/code&gt; If you know the intended behavior but want a first draft quickly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both paths converge to the same shipping workflow later.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/001-1_ejDftHO8TiktbPMim-EVqA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="20-build-extension-1-manually"&gt;&lt;strong&gt;2.0: Build Extension #1 Manually&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For the first pass, we will create something small and observable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our Goal&lt;/strong&gt;: add a command named &lt;code&gt;Toggle Right Sidebar&lt;/code&gt; to the command palette.&lt;/p&gt;
&lt;h3 id="21-create-the-plugin-file"&gt;&lt;strong&gt;2.1: Create the plugin file&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Launch &lt;strong&gt;Start from File&lt;/strong&gt;. This comes with a dummy template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/application&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello-world:plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;autoStart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="22-fill-the-missing-pieces-using-the-right-sidebar"&gt;&lt;strong&gt;2.2: Fill the missing pieces using the right sidebar&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now build the plugin in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;Tokens&lt;/code&gt;section in the right sidebar, search &lt;code&gt;ICommandPalette&lt;/code&gt; and click insert ( + icon ), so Plugin Playground adds token import and dependency wiring.&lt;/li&gt;
&lt;li&gt;Add a simple command skeleton inside the &lt;strong&gt;activate&lt;/strong&gt; function:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-first-playground-plugin:toggle-sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Toggle Right Sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// place cursor here&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;In &lt;code&gt;Commands&lt;/code&gt;, search &lt;code&gt;application:toggle-right-area&lt;/code&gt;, place your cursor inside &lt;code&gt;execute&lt;/code&gt; of the added command ( see above ) and use &lt;code&gt;Insert in selection&lt;/code&gt; This adds at the cursor position:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;application:toggle-right-area&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Register the command in the palette:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AAA&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After those edits, your file should look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ICommandPalette&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/apputils&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/application&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello-world:plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;autoStart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ICommandPalette&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ICommandPalette&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-first-playground-plugin:toggle-sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Toggle Right Sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;application:toggle-right-area&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AAA&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Uses the typed plugin template (&lt;code&gt;JupyterFrontEndPlugin&amp;lt;void&amp;gt;&lt;/code&gt;) and typed &lt;code&gt;activate&lt;/code&gt; parameters. Imports &lt;code&gt;ICommandPalette&lt;/code&gt; so the plugin can add entries to the Command Palette.&lt;/li&gt;
&lt;li&gt;Registers a new command (&lt;code&gt;my-first-playground-plugin:toggle-sidebar&lt;/code&gt;) and executes &lt;code&gt;application:toggle-right-area&lt;/code&gt; when your command runs.&lt;/li&gt;
&lt;li&gt;Adds your command to the palette under the category &lt;code&gt;AAA&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="23-load-it-and-verify-behavior"&gt;&lt;strong&gt;2.3: Load it and verify behavior&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Click &lt;strong&gt;Load Current File As Extension or click the run button in the toolbar&lt;/strong&gt;, and open Command Palette.&lt;/p&gt;
&lt;p&gt;When you run &lt;strong&gt;Load Current File As Extension&lt;/strong&gt;, Plugin Playground compiles the active file and registers the plugin object into the live JupyterLab session. During iteration, if a plugin with the same &lt;strong&gt;id&lt;/strong&gt; already exists, Plugin Playground attempts to deactivate and replace it so reload loops stay fast.&lt;/p&gt;
&lt;h3 id="expected-result"&gt;&lt;strong&gt;Expected result&lt;/strong&gt;:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Toggle Right Sidebar&lt;/strong&gt; appears in the command palette ( open command palette by going through &lt;strong&gt;view -&amp;gt; Activate command palette&lt;/strong&gt; or Press &lt;strong&gt;Accel&lt;/strong&gt; + &lt;strong&gt;shift&lt;/strong&gt; + &lt;strong&gt;C&lt;/strong&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/002-1_RXMnfrUXeu1q7AkukZEF_A.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Running it hides/shows the JupyterLab right sidebar.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="30-build-with-ai"&gt;&lt;strong&gt;3.0: Build with AI&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you want a faster draft, use AI assistance for the same extension workflow.&lt;/p&gt;
&lt;p&gt;Goal: add the &lt;strong&gt;Show Active Notebook Cell Count&lt;/strong&gt; command with a friendly no-notebook fallback.&lt;/p&gt;
&lt;h3 id="31-give-ai-a-precise-request"&gt;&lt;strong&gt;3.1: Give AI a precise request&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;strong&gt;Build with AI&lt;/strong&gt; and give a prompt:&lt;/p&gt;
&lt;p&gt;for example, something like:-&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Create a JupyterLab plugin named &amp;quot;active-notebook-cell-counter&amp;quot;.
Add a command &amp;quot;Show Active Notebook Cell Count&amp;quot;.
If no notebook is active, show a friendly dialog message.
If a notebook is active, show the current cell count in a dialog.
Register the command in the command palette under category &amp;quot;Playground Demo&amp;quot;.
Export default plugin object.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;This gives AI a concrete plugin goal, command name, and fallback behavior requirements.&lt;/p&gt;
&lt;h3 id="32-use-ai-deterministic-inserts-together"&gt;&lt;strong&gt;3.2: Use AI + deterministic inserts together&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;strong&gt;Commands&lt;/strong&gt; tab:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Insert in selection&lt;/strong&gt; for predictable boilerplate insertion.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prompt AI to insert&lt;/strong&gt; when placement context is tricky.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;{n}&lt;/strong&gt; when you need argument shape clarity ( Inspect the command signature before insertion: it shows expected argument names/types and return details).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use &lt;strong&gt;Insert in selection&lt;/strong&gt; for predictable snippets, and switch to AI when insertion location or code adaptation depends on the surrounding context.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/003-1_HAs9F-B9l77KolI3ZZTH3g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="40-iterate-faster-before-handoff"&gt;&lt;strong&gt;4.0: Iterate Faster Before Handoff&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once the extensions run, enable &lt;strong&gt;Run on save&lt;/strong&gt;(from the toolbar or from the settings) while polishing labels, command categories, and small behaviour details.&lt;/p&gt;
&lt;p&gt;With &lt;strong&gt;Run on save&lt;/strong&gt;, each file save triggers a reload loop for that plugin file, which makes label tweaks, command grouping changes, and message-copy iteration much faster than manual reload cycles. And with enabling the global setting (&lt;strong&gt;Load as extension on save&lt;/strong&gt;), this behaviour is present for all files by default.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/004-1_wfxS4HhzRfU5-bbKYfPonA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="50-share-for-review"&gt;&lt;strong&gt;5.0: Share for Review&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before packaging, you can ask a teammate to review behaviour or just share links for the extension for a quick look using:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Share a single file&lt;/strong&gt; when sharing is about one plugin file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Share a package&lt;/strong&gt; when your logic spans several files.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps review lightweight: collaborators can inspect and discuss behaviour before you commit to repository structure, CI, and release wiring.&lt;/p&gt;
&lt;h3 id="60-export-as-wheel-and-validate-in-binder"&gt;&lt;strong&gt;6.0: Export as Wheel and Validate in Binder&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now test as an installable artifact in a separate environment.&lt;/p&gt;
&lt;h3 id="61-export-whl"&gt;&lt;strong&gt;6.1: Export &lt;code&gt;.whl&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;From the export dropdown ( in toolbar ), choose &lt;strong&gt;Export as Python package (.whl)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="62-install-in-binder"&gt;&lt;strong&gt;6.2: Install in Binder&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open JupyterLab on Binder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upload the downloaded wheel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;force-reinstall&lt;span class="w"&gt; &lt;/span&gt;./your_exported_package.whl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does-1"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;Installs the wheel you just exported from Plugin Playground into Binder. (Uses &lt;strong&gt;— force-reinstall&lt;/strong&gt; so Binder replaces any previously installed copy with your newest build). Then refresh JupyterLab and verify commands.&lt;/p&gt;
&lt;h3 id="70-export-as-zip-and-create-a-github-repository"&gt;&lt;strong&gt;7.0: Export as Zip and Create a GitHub Repository&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;After runtime validation, move to normal engineering workflows.&lt;/p&gt;
&lt;h3 id="71-export-zip"&gt;&lt;strong&gt;7.1: Export &lt;code&gt;.zip&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Choose &lt;code&gt;**Export as archive (.zip)**&lt;/code&gt; from the toolbar export dropdown.&lt;/p&gt;
&lt;h3 id="72-bootstrap-repository"&gt;&lt;strong&gt;7.2: Bootstrap repository&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Unzip locally, create an empty GitHub repo, then run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;init&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Initial extension scaffold from Plugin Playground&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/&amp;lt;your-org-or-user&amp;gt;/my-jlab-extension.git&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does-2"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;Initializes a new Git repository from the exported scaffold and creates an initial commit that captures the generated extension baseline.&lt;/p&gt;
&lt;p&gt;Now your prototype is in a standard repo flow with PRs, CI, and release automation.&lt;/p&gt;
&lt;h3 id="whats-next"&gt;What’s Next&lt;/h3&gt;
&lt;p&gt;Plugin Playground is still evolving. We’re actively working on the &lt;a href="https://github.com/jupyterlab/plugin-playground/pull/225"&gt;LSP integration&lt;/a&gt; for JupyterLite to bring code intelligence autocomplete and diagnostics directly into the editor, an &lt;a href="https://github.com/jupyterlab/plugin-playground/pull/231"&gt;“Ask AI” button on log errors&lt;/a&gt; that lets you send error context directly into the AI chat for instant debugging help. In the future, we would like to explore &lt;a href="https://github.com/jupyterlab/plugin-playground/issues/139"&gt;git integration&lt;/a&gt; to snapshots every successful load, paired with a &lt;a href="https://github.com/jupyterlab/plugin-playground/issues/140"&gt;built-in diff viewer&lt;/a&gt; to compare any snapshot against your current file. If you have any suggestions or encounter any problems, please let us know by opening &lt;a href="https://github.com/jupyterlab/plugin-playground/issues"&gt;an issue on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="acknowledgements"&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We are grateful to the &lt;a href="https://jupyterfoundation.org/join/"&gt;Jupyter Foundation&lt;/a&gt; and its &lt;a href="https://jupyterfoundation.org/members/"&gt;members&lt;/a&gt; for sponsoring the development of Plugin Playground as part of &lt;a href="https://jupyterfoundation.org/community-funding-proposals/"&gt;Community Funding Proposals 2025&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We thank Jeremy Tuloup, Nick Bollweg, and Nicolas Brichet for providing inspiration, review, and advice across our work on the plugin and its dependencies. Finally, this work would not be possible without the &lt;a href="https://github.com/jupyterlab/plugin-playground/graphs/contributors?from=01%2F01%2F2020&amp;amp;to=18%2F12%2F2024"&gt;authors of earlier&lt;/a&gt; iterations of Plugin Playground.&lt;/p&gt;
&lt;h3 id="about-the-developers"&gt;&lt;strong&gt;About the&lt;/strong&gt; Developers&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/anuj-singh-2b2ab6206/"&gt;&lt;strong&gt;Anuj Singh&lt;/strong&gt;&lt;/a&gt; is an OSS intern at OpenTeams. Anuj implemented Plugin Playground v1.0.0 and contributed to its dependencies during his internship at &lt;a href="https://openteams.com/"&gt;OpenTeams&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/smera-goel/"&gt;&lt;strong&gt;Smera Goel&lt;/strong&gt;&lt;/a&gt; is a UI/UX designer at &lt;a href="https://quansight.com/"&gt;Quansight&lt;/a&gt;. Smera contributed to the user experience and design direction of Plugin Playground.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/michal-krassowski/"&gt;&lt;strong&gt;Michał Krassowski&lt;/strong&gt;&lt;/a&gt; is a Senior Software Engineer at &lt;a href="https://openteams.com/"&gt;OpenTeams&lt;/a&gt;. Mike provided guidance and direction for the development of the Plugin Playground.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Blog post on &lt;a href="https://openteams.com/plugin-playground-ai-integration/"&gt;how and why we integrated AI functions in the playground&lt;/a&gt; extension&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlab/plugin-playground/blob/main/CHANGELOG.md"&gt;Changelog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlab/plugin-playground/blob/main/README.md"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="AI"/><category term="extensions"/><category term="JupyterLab"/><category term="JupyterLite"/></entry><entry><title>JupyterLite Officially Joins Project Jupyter!</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/" rel="alternate"/><published>2026-02-12T16:14:00+00:00</published><updated>2026-02-19T12:26:00+00:00</updated><author><name>Project Jupyter</name></author><id>tag:jasongrout.github.io,2026-02-12:/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/</id><summary type="html">&lt;p&gt;We are thrilled to announce that JupyterLite is now an official part of Project Jupyter. This milestone marks a significant step forward…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/images/001-1_1mHVBUr6tB3TP0Z1ujvdjw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;We are thrilled to announce that JupyterLite is now an official part of Project Jupyter. This milestone marks a significant step forward for interactive computing in the browser and strengthens JupyterLite’s role within the Jupyter ecosystem.&lt;/p&gt;
&lt;h2 id="what-is-jupyterlite"&gt;What is JupyterLite?&lt;/h2&gt;
&lt;p&gt;JupyterLite is a JupyterLab distribution that runs entirely in your web browser. Kernels execute directly in the browser using WebAssembly, eliminating the need for an application server. This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Instant access&lt;/strong&gt;: Start computing with a single click: no prior Python setup, environment configuration, or server management required.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Host thousands of concurrent users from a static website (e.g., GitHub Pages) with zero per-user server costs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Privacy and portability&lt;/strong&gt;: Code and data remain in the user’s browser, making it ideal for embedding in documentation, tutorials, and interactive demos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JupyterLite greatly expands the options available in the Jupyter ecosystem for &lt;strong&gt;education&lt;/strong&gt;, &lt;strong&gt;documentation&lt;/strong&gt;, and &lt;strong&gt;demos&lt;/strong&gt;, where reducing friction is critical.&lt;/p&gt;
&lt;p&gt;As Brian Granger reminded us during his &lt;a href="https://youtu.be/IJO7_v7GEVc?si=8_0bMM39ci_QulYb&amp;amp;t=337"&gt;JupyterCon 2025 keynote&lt;/a&gt;, Jupyter’s mission is “to empower people of all backgrounds to think, collaborate, and share knowledge using computational storytelling.” From this perspective, JupyterLite is a logical and key next step to take, letting Jupyter take advantage of the impressive strides made in recent years by the WebAssembly/JavaScript ecosystem (in reach and capability) to advance this mission. JupyterLite is an excellent complement to other forms of accessing Jupyter (whether through a local Python installation or a hosted infrastructure service), that facilitates new use cases and lowers barriers in many others.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screencast of JupyterLite in action, showing a live jupyter notebook with widgets and data visualisation." src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/images/002-1_OEJksEVfQVZugt_d6EYXBQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Interactive computing in the browser with JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="the-journey-of-jupyterlite"&gt;The Journey of JupyterLite&lt;/h2&gt;
&lt;p&gt;JupyterLite’s development began in 2021, led by &lt;strong&gt;Jeremy Tuloup&lt;/strong&gt; at &lt;strong&gt;QuantStack&lt;/strong&gt;. Over the past four years, it has benefited from the dedication of several other team members, Martin Renou, Trung Le, and Ian Thomas — as well as invaluable contributions from &lt;strong&gt;community members&lt;/strong&gt; like Nick Bollweg and many others.&lt;/p&gt;
&lt;p&gt;Beyond the JupyterLite repository, the project includes &lt;strong&gt;comprehensive tooling&lt;/strong&gt; for creating in-browser language kernels based on &lt;strong&gt;xeus&lt;/strong&gt;. These kernels support languages like &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;R&lt;/strong&gt;, &lt;strong&gt;C++&lt;/strong&gt;, &lt;strong&gt;GNU Octave&lt;/strong&gt;, &lt;strong&gt;Lua&lt;/strong&gt;, and &lt;strong&gt;SQLite&lt;/strong&gt;, sharing the same codebase as their backend counterparts, developed by Thorsten Beier, Isabel Paredes, Johan Mabille, and Antoine Prouvost. These kernels are built upon the &lt;strong&gt;emscripten-forge&lt;/strong&gt; software distribution for WebAssembly. The project also includes a Python kernel based on &lt;strong&gt;Pyodide&lt;/strong&gt;, and kernels for JavaScript and &lt;a href="http://p5.js"&gt;p5.js&lt;/a&gt;. This architecture means that the same language-agnostic model for kernels that Jupyter pioneered over a decade ago, carries over to the WebAssembly world.&lt;/p&gt;
&lt;p&gt;The JupyterLite GitHub organization also features the &lt;strong&gt;JupyterLite Terminal&lt;/strong&gt;, a terminal and shell emulator that runs entirely in the browser and was developed by Ian Thomas. It enables the use of basic Unix commands like &lt;strong&gt;grep&lt;/strong&gt;, &lt;strong&gt;sed&lt;/strong&gt;, &lt;strong&gt;cat&lt;/strong&gt;, &lt;strong&gt;touch&lt;/strong&gt;, and even &lt;strong&gt;vim&lt;/strong&gt; or &lt;strong&gt;nano&lt;/strong&gt;, all built to WebAssembly.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screenshot of the JupyterLite terminal emulator, showing some basic Unix commands" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/images/003-1_8hPGoJK8chK2uEVlL9ig9Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLite terminal emulator, with basic Unix commands&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;JupyterLite now powers the &lt;strong&gt;official Jupyter website&lt;/strong&gt; and is used by projects like &lt;strong&gt;numpy.org&lt;/strong&gt;, &lt;strong&gt;sympy.org&lt;/strong&gt;. It also underlies services such as &lt;a href="https://www.jupytereverywhere.org/"&gt;&lt;strong&gt;Jupyter Everywhere&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://notebook.link/"&gt;&lt;strong&gt;notebook.link&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="a-natural-fit-with-jupyterlab"&gt;A Natural Fit with JupyterLab&lt;/h2&gt;
&lt;p&gt;JupyterLite has always been closely tied to JupyterLab. It is increasingly becoming a set of JupyterLab extensions that replace core plugins to manage kernels, settings, and content in the browser. With contributors overlapping significantly with the Jupyter Frontends group, this integration formalizes what many in the community already recognized: JupyterLite is a core part of the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;The proposal to transfer JupyterLite to the Jupyter governance received strong support from the Jupyter community and by the &lt;a href="https://github.com/jupyterlab/frontends-team-compass/issues/290"&gt;Jupyter Frontends council&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="better-integration-with-the-rest-of-jupyter"&gt;Better integration with the rest of Jupyter&lt;/h2&gt;
&lt;p&gt;With JupyterLite now being an official part of Jupyter, it will be easier to find areas for better integration with other aspects of our ecosystem: we can reduce duplication, smooth out the documentation and model for creating and using both “traditional” (server-hosted) and WebAssembly kernels, and make JupyterLite a natural instant-access component of &lt;a href="https://mystmd.org/guide/in-page-execution#jupyterlite"&gt;MyST/JupyterBook-based sites&lt;/a&gt;, and more.&lt;/p&gt;
&lt;p&gt;Mission-wise, JupyterLite is a natural next step for Jupyter, and having it be an official part of the project makes it much easier for the community to integrate its benefits throughout. We hope you’ll try it, use it and contribute to its growth!&lt;/p&gt;
&lt;h2 id="try-it-in-your-browser"&gt;Try it in your browser&lt;/h2&gt;
&lt;p&gt;If you would like to try JupyterLite in your browser, click on the following link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyter.org/try-jupyter/"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/jupyterlite-officially-joins-project-jupyter/images/004-1_EUSdzza6CGF6EA195jkzCA.webp" alt="Try lite now." loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgments-and-team-credits"&gt;Acknowledgments and team credits&lt;/h2&gt;
&lt;p&gt;JupyterLite’s success is thanks to the support of &lt;strong&gt;individuals and organizations&lt;/strong&gt; who believed in its vision. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QuantStack&lt;/strong&gt;, for their continued investment in the project since 2021 and the broader Jupyter ecosystem,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;, for funding improvements to JupyterLite by QuantStack since 2023,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Gates Foundation&lt;/strong&gt;, for funding the development of the xeus-R kernel and its port to WebAssembly,&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Pyodide&lt;/strong&gt; project, whose pioneering work on Python in the browser via WebAssembly made JupyterLite possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Key developers to this stack include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jeremy Tuloup&lt;/strong&gt; (Director at QuantStack, creator of JupyterLite, and JupyterLab maintainer),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nicholas Bollweg&lt;/strong&gt; (JupyterLite maintainer and #2 all-time committer),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Martin Renou&lt;/strong&gt; (Director at QuantStack, JupyterLite maintainer, responsible for integrating xeus with emscripten-forge, and creator of JupyterLite-Sphinx),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thorsten Beier&lt;/strong&gt; (Software developer at QuantStack, lead developer of emscripten-forge, and xeus-lite),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Isabel Paredes&lt;/strong&gt; (Software developer at QuantStack, led the packaging of R and GNU Octave in Emscripten-forge),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anutosh Bhat&lt;/strong&gt; (Software developer at QuantStack, C++ kernel in the browser),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ian Thomas&lt;/strong&gt; (Software developer at QuantStack, creator of the JupyterLite terminal, and the cockle shell emulator),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt; (Director at QuantStack, creator of xeus),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agriya Khetarpal&lt;/strong&gt; (Pyodide contributor, JupyterLite-Sphinx maintainer),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Albert Steppi&lt;/strong&gt; (JupyterLite-Sphinx maintainer).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you for being part of this journey, we cannot wait to see what you build with JupyterLite!&lt;/p&gt;
&lt;p&gt;— this post was coauthored by Jérémy Tuloup, Sylvain Corlay, and Fernando Pérez&lt;/p&gt;
</content><category term="education"/><category term="JupyterLite"/><category term="WebAssembly"/></entry><entry><title>JupyterLite 0.7 is released! 🎉</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/" rel="alternate"/><published>2025-12-04T22:11:00+00:00</published><updated>2025-12-04T22:11:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2025-12-04:/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/</id><summary type="html">&lt;p&gt;JupyterLite is a Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://jupyterlite.readthedocs.io/"&gt;JupyterLite&lt;/a&gt; is a Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy as JupyterLite sites are just static websites.&lt;/p&gt;
&lt;p&gt;The new 0.7 release includes a number of new features, bug fixes, and enhancements. This release also brings significant improvements to the user experience and new customization options for JupyterLite deployments.&lt;/p&gt;
&lt;h2 id="workspace-support"&gt;Workspace Support&lt;/h2&gt;
&lt;p&gt;Support for workspaces is now available in JupyterLite. Workspaces allow you to organize your notebooks and files into separate workspace environments, making it easier to switch between different projects or contexts.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Workspaces are now supported in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/001-0_9Zx1pB5PRtEUdrYR.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Workspaces are now supported in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The UI layout is also now automatically persisted across sessions. When you return to JupyterLite, your panel arrangements, open files, and workspace configuration will be restored exactly as you left them.&lt;/p&gt;
&lt;h2 id="audio-and-video-playback"&gt;Audio and Video Playback&lt;/h2&gt;
&lt;p&gt;JupyterLite now includes built-in audio and video viewers, allowing users to open audio and video files directly from within the UI.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Open audio and video files in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/002-0__FaFC2jYOUzmdmzN.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Open audio and video files in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="basic-interrupt-functionality"&gt;Basic Interrupt Functionality&lt;/h2&gt;
&lt;p&gt;The interrupt button now cancels the execution of cells that are scheduled to run after the currently executing cell. While this doesn’t interrupt the currently running cell itself, it provides better control when executing multiple cells in sequence.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The stop button now cancels the execution of scheduled cells" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/003-0_BNaPV7T03CPtnemP.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The stop button now cancels the execution of scheduled cells&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="basic-notebook-export"&gt;Basic Notebook Export&lt;/h2&gt;
&lt;p&gt;JupyterLite now includes basic notebook export functionality through a custom export plugin. Users can export notebooks to different formats including &lt;code&gt;.ipynb&lt;/code&gt; (notebook format) and script formats directly from the File menu.&lt;/p&gt;
&lt;p&gt;Additionally, JupyterLite provides extension points for third-party extensions to register custom exporters. This enables the development of additional export formats, such as PDF, in future extensions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: there is currently no built-in support for PDF export, but this may be added in the future via a third-party extension.&lt;/em&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Basic notebook exports are now available" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/004-0_lqn02-318xj6qHVX.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Basic notebook exports are now available&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="file-menu-download-option"&gt;File Menu Download Option&lt;/h2&gt;
&lt;p&gt;A download entry has been added to the File menu, making it easier to download files from your JupyterLite environment.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Download a notebook using the file menu entry" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/005-0_yaURYoXXA2Ujf_Ti.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Download a notebook using the file menu entry&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="enhanced-markdown-support"&gt;Enhanced Markdown Support&lt;/h2&gt;
&lt;p&gt;Markdown cells now support displaying base64-encoded images stored in the browser’s local storage. This makes it easier to include images in your notebooks without requiring external hosting or network access.&lt;/p&gt;
&lt;h2 id="jupyterlab-45-and-notebook-75"&gt;JupyterLab 4.5 and Notebook 7.5&lt;/h2&gt;
&lt;p&gt;JupyterLite 0.7.0 is built on top of JupyterLab 4.5.0 and Notebook 7.5.0, and brings many of the respective improvements and bug fixes. Check out the &lt;a href="/posts/2025/jupyterlab-4-5-and-notebook-7-5-are-available/"&gt;announcement post&lt;/a&gt; to learn more about these new releases.&lt;/p&gt;
&lt;p&gt;JupyterLab 4.5 notably includes significant &lt;a href="https://jupyterlab.readthedocs.io/en/latest/getting_started/changelog.html#performance-and-windowing"&gt;notebook performance improvements&lt;/a&gt;, such as the new optimized cell rendering with the new &lt;code&gt;contentVisibility&lt;/code&gt; windowing mode.&lt;/p&gt;
&lt;h2 id="walkthrough-video"&gt;Walkthrough video&lt;/h2&gt;
&lt;p&gt;For a more interactive tour of the new features, watch the video below:&lt;/p&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/utNtppEyK5Y" title="New features in JupyterLite 0.7💡" width="560" height="315" style="aspect-ratio: 560 / 315" loading="lazy" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2 id="try-it-in-your-browser"&gt;Try it in your browser&lt;/h2&gt;
&lt;p&gt;If you would like to try the new release in your browser, click on the following link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyter.org/try-jupyter/lab/"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/006-0_nbMbf0ppEOPuY0Ur.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href="https://x.com/TechAtBloomberg"&gt;Bloomberg&lt;/a&gt; for funding the work on JupyterLite at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;, and for their ongoing support for JupyterLite in general.&lt;/p&gt;
&lt;p&gt;Many thanks to all the contributors to JupyterLite. The list below is based on GitHub usernames for the 0.7.0 release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New contributors: @rgaiacs, @tan-i-ham&lt;/li&gt;
&lt;li&gt;Returning contributors: @DerThorsten, @bollwyvl, @diegofcornejo, @ianthomas23, @jtpio, @krassowski, @martinRenou&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you to the extended group of contributors in Jupyter Frontends and related project in the Jupyter ecosystem 💛&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-7-is-released/images/007-0_5lm4W0GV98Yy1q9l.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLite"/><category term="releases"/><category term="WebAssembly"/></entry><entry><title>GNU Octave Meets JupyterLite: Compute Anywhere, Anytime!</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/gnu-octave-meets-jupyterlite-compute-anywhere-anytime/" rel="alternate"/><published>2025-10-16T15:04:00+00:00</published><updated>2025-10-16T15:04:00+00:00</updated><author><name>Isabel Paredes</name></author><id>tag:jasongrout.github.io,2025-10-16:/medium-archive/pelican/posts/2025/gnu-octave-meets-jupyterlite-compute-anywhere-anytime/</id><summary type="html">&lt;p&gt;We are thrilled to announce the newest member of our JupyterLite kernel ecosystem: Xeus-Octave. Xeus-Octave allows you to run GNU Octave…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/gnu-octave-meets-jupyterlite-compute-anywhere-anytime/images/001-1__Ha5vHmLH66mSH3WFg1fxg.jpg" alt="Octave-lite logo" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;We are thrilled to announce the newest member of our JupyterLite kernel ecosystem: &lt;strong&gt;Xeus-Octave&lt;/strong&gt;. Xeus-Octave allows you to run GNU Octave code directly on your browser. GNU Octave is a free and open-source Scientific Programming Language that can be used to run Matlab scripts. In this article, we present the challenges encountered when targeting WebAssembly, the current state of the Xeus-Octave kernel, and the future plans for expanding the GNU Octave ecosystem.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Earlier this year, we introduced the JupyterLite kernel for R, &lt;a href="/posts/2025/r-in-the-browser-announcing-our-webassembly/"&gt;&lt;strong&gt;Xeus-R-Lite&lt;/strong&gt;&lt;/a&gt;. Much like R, cross-compiling GNU Octave to WebAssembly required the same custom toolchain to enable the compilation of Fortran code, combining LLVM Flang and Emscripten.&lt;/p&gt;
&lt;p&gt;Similar to many other mathematically oriented language packages, GNU Octave requires a BLAS/LAPACK implementation. Fortunately, OpenBLAS and the Netlib implementations of BLAS/LAPACK had already been added to the emscripten-forge WebAssembly distribution. Initially, OpenBLAS was the preferred implementation, but for the successful compilation of Octave, Netlib LAPACK was selected as it presented fewer hurdles during the build process.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cross-compilation-of-gnu-octave"&gt;Cross-Compilation of GNU Octave&lt;/h2&gt;
&lt;p&gt;One of the complications of cross-compiling Octave to WebAssembly, which had not been encountered with the R source code, was the extensive use of Fortran common symbols blocks in the internal libraries of Octave such as odepack.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;liboctave&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;odepack&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;slsode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;-----------------------------------------------------------------------&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Common&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subroutine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;whose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;must&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;preserved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;routine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;own&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;communicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;between&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subroutines&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SLS001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;declared&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subroutines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SLSODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SINTDY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSTODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SPREPJ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSOLSY&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;replaced&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arrays&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Common&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;declarations&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;routines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;those&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;-----------------------------------------------------------------------&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;COMMON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;SLS001&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CONIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CRATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ELCO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;HOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RMAX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TESCO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;CCMAX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EL0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HMIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HMXI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UROUND&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MXSTEP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MXHNIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NHNIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NSLAST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NYH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;IALTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IPUP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LMAX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MEO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NQNYH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NSLP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ICF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IERPJ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IERSL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JCUR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSTART&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;KFLAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;LYH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LEWT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LACOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LSAVF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LWM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LIWM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;METH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MITER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;MAXORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MAXCOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MSBP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MXNCF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NFE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NJE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NQU&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Initially, it was not possible to cross-compile these common blocks to WebAssembly because the latest version of LLVM (v20 at the time of testing) did not support common symbol linkage.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Source: llvm/lib/MC/MCWasmStreamer.cpp&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;MCWasmStreamer::emitCommonSymbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MCSymbol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="n"&gt;Align&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ByteAlignment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;llvm_unreachable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Common symbols are not yet implemented for Wasm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As a temporary solution, LLVM was patched with the help of &lt;a href="https://github.com/serge-sans-paille"&gt;Serge Guelton&lt;/a&gt; to simulate common symbols as weak symbols.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
&lt;span class="w"&gt; &lt;/span&gt;                                     Align ByteAlignment) {
&lt;span class="gd"&gt;-  llvm_unreachable(&amp;quot;Common symbols are not yet implemented for Wasm&amp;quot;);&lt;/span&gt;
&lt;span class="gi"&gt;+  auto *Symbol = cast&amp;lt;mcsymbolwasm&amp;gt;(S);&lt;/span&gt;
&lt;span class="gi"&gt;+  getAssembler().registerSymbol(*Symbol);&lt;/span&gt;
&lt;span class="gi"&gt;+  Symbol-&amp;gt;setWeak(true);&lt;/span&gt;
&lt;span class="gi"&gt;+  Symbol-&amp;gt;setExternal(true);&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A proper solution to enable support of common symbols is currently in progress and will likely be included in the next release of LLVM v22 (see &lt;a href="https://github.com/llvm/llvm-project/pull/151478"&gt;llvm-project/pull/151478&lt;/a&gt;). For curious readers, the patched version of LLVM can be found &lt;a href="https://github.com/IsabelParedes/llvm-project/releases/download/v20.1.7_emscripten-wasm32/llvm_emscripten-wasm32-20.1.7-h2e33cc4_5.tar.bz2"&gt;here&lt;/a&gt; (linux only).&lt;/p&gt;
&lt;p&gt;In addition to the patches for LLVM, GNU Octave required a few minor modifications to target WebAssembly; mainly this entailed disabling the GUI functionalities and consolidating the Fortran function signatures and calling conventions. A full list of patches can be found in the &lt;a href="https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten/octave/patches"&gt;recipe directory&lt;/a&gt; on emscripten-forge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="xeus-octave"&gt;Xeus-Octave&lt;/h2&gt;
&lt;p&gt;Once GNU Octave had been successfully packaged for WebAssembly, bringing &lt;a href="https://github.com/jupyter-xeus/xeus-octave"&gt;Xeus-Octave&lt;/a&gt; to JupyterLite was a simple matter of adding a recipe to &lt;a href="https://github.com/emscripten-forge/recipes/"&gt;emscripten-forge&lt;/a&gt;!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A JupyterLite notebook demonstrating plots with Xeus-Octave" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/gnu-octave-meets-jupyterlite-compute-anywhere-anytime/images/002-1_lKsfXJGc7_tM1ONU5Ej0Ng.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://jupyter-xeus.github.io/xeus-octave/lab/?path=xeus-octave-wasm.ipynb"&gt;&lt;strong&gt;Try Xeus-Octave in JupyterLite!&lt;/strong&gt;&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;h2 id="future-work"&gt;Future Work&lt;/h2&gt;
&lt;p&gt;For our next steps, the team is planning on expanding the Octave ecosystem by adding Octave packages to both conda-forge and emscripten-forge. The packaging work will require defining a process where Octave packages can be installed in predetermined conda environments, perhaps with some minor modifications to the existing pkg utility.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="about-the-author"&gt;About the Author&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Isabel Paredes&lt;/strong&gt;, who led the charge on bringing GNU Octave to emscripten-forge, is a senior scientific software developer at QuantStack. Prior to working on this project, she focused on porting the R programming language and the Robot Operating System (ROS) framework to WebAssembly.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;This project synthesizes work from many open-source contributors.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/emscripten-forge/recipes/"&gt;&lt;strong&gt;Emscripten-forge&lt;/strong&gt;&lt;/a&gt;, the distribution of conda packages for WebAssembly, was created by Thorsten Beier, who continues to lead the project. Many recipes were contributed by Isabel Paredes, Anutosh Bhat, Martin Renou, Ian Thomas, Wolf Vollprecht, and Johan Mabille.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jupyterlite.readthedocs.io/en/stable/?badge=latest"&gt;&lt;strong&gt;JupyterLite&lt;/strong&gt;&lt;/a&gt;, the Jupyter distribution that runs entirely in the web browser, was created by Jeremy Tuloup.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus"&gt;&lt;strong&gt;Xeus&lt;/strong&gt;&lt;/a&gt;, the C++ library implementing the Jupyter kernel protocol, enabling a custom communication layer, and is foundational to kernels like xeus-r, xeus-python, running in JupyterLite, was created by Johan Mabille and is maintained by a broader team including Martin Renou, Sylvain Corlay, and Thorsten Beier, who worked on the first integration with JupyterLite.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-octave"&gt;&lt;strong&gt;Xeus-Octave&lt;/strong&gt;&lt;/a&gt;, the Xeus-based Jupyter kernel for GNU Octave, was created by Giulio Girardi and Antoine Prouvost.&lt;/li&gt;
&lt;/ul&gt;
</content><category term="JupyterLite"/><category term="kernels"/><category term="WebAssembly"/><category term="xeus"/></entry><entry><title>JupyterLite 0.6.0 is released! 🎉</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/" rel="alternate"/><published>2025-06-12T15:04:00+00:00</published><updated>2025-06-12T15:04:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2025-06-12:/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/</id><summary type="html">&lt;p&gt;JupyterLite 0.6.0 includes a number of new features, bug fixes, and enhancements and significant improvements to the user interface.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://jupyterlite.readthedocs.io/"&gt;JupyterLite&lt;/a&gt; is an unofficial Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy as JupyterLite sites are just static websites.&lt;/p&gt;
&lt;p&gt;The new 0.6.0 release includes a number of new features, bug fixes, and enhancements. This release also brings significant improvements to the user experience and new customization options for JupyterLite deployments.&lt;/p&gt;
&lt;h2 id="interactive-input-support"&gt;Interactive Input Support&lt;/h2&gt;
&lt;p&gt;Support for kernel &lt;code&gt;stdin&lt;/code&gt; requests is now available, enabling interactive input functions like Python’s &lt;code&gt;input()&lt;/code&gt;and &lt;code&gt;getpass()&lt;/code&gt; in notebooks. This fixes a long standing limitation of JupyterLite when executing the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;What is your name? &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Using input() and getpass() in a JupyterLite notebook" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/001-0_KpoAVYq3Wi5pBWd2.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using input() and getpass() in a JupyterLite notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Since &lt;code&gt;input()&lt;/code&gt;is used quite extensively in introductory Python courses, this new feature should help make teaching with JupyterLite easier!&lt;/p&gt;
&lt;h2 id="new-repl-options"&gt;New REPL Options&lt;/h2&gt;
&lt;p&gt;The REPL exposes new options as URL parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;promptCellPosition&lt;/code&gt; — place the prompt cell to the top, left, right or bottom&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clearCodeContentOnExecute&lt;/code&gt; — when disabled, the code submitted for execution remains in the code editor, allowing for further edits.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hideCodeInput&lt;/code&gt; — when enabled, only the execution output is shown in the console.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clearCellsOnExecute&lt;/code&gt; — when enabled, only the last cell is displayed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showBanner&lt;/code&gt; — when disabled, hide the banner for the kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Toggling all the new options transforms the console into an interactive editor resembling an ephemeral notebook with a single cell.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A single executable cell in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/002-0_Q20JOZb2NQGfLeUT.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;A single executable cell in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In addition to these URL parameters, the toolbar includes new items to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Execute code&lt;/li&gt;
&lt;li&gt;Restart the kernel&lt;/li&gt;
&lt;li&gt;Clear the cells&lt;/li&gt;
&lt;li&gt;Switch the kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out the &lt;a href="https://jupyterlite.readthedocs.io/en/latest/quickstart/embed-repl.html"&gt;REPL documentation&lt;/a&gt; for more details on how to use these parameters.&lt;/p&gt;
&lt;h2 id="kernel-indicator-and-logs"&gt;Kernel Indicator and Logs&lt;/h2&gt;
&lt;p&gt;A new notebook toolbar item has been added to show the kernel status with three different states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Loading: the kernel is starting or performing some actions&lt;/li&gt;
&lt;li&gt;Success: the kernel is ready to execute code&lt;/li&gt;
&lt;li&gt;Failure: an error occurred while starting the kernel or during execution, and likely requires a restart of the kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This new toolbar item gives users better visibility into the kernel’s status and allows them to view the kernel logs by clicking on the toolbar item to open the log console.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Kernel logs can help troubleshoot JupyterLite issues" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/003-0_GaXGJVtpTFN0_nPM.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Kernel logs can help troubleshoot JupyterLite issues&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="clear-browser-data"&gt;Clear Browser Data&lt;/h2&gt;
&lt;p&gt;By default JupyterLite stores user-created notebooks and settings in the browser.&lt;/p&gt;
&lt;p&gt;In previous versions, users had to manually clear the data using the browser developer tools. With JupyterLite 0.6.0 it is now possible to clear the browser data from the UI by clicking on the &lt;code&gt;Help &amp;gt; Clear Browser Data&lt;/code&gt; menu item. The confirmation dialog will show options to clear settings and contents.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The confirmation dialog for clearing browser data" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/004-0_zRK_R5mnRajeqlpa.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The confirmation dialog for clearing browser data&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="loading-indicator"&gt;Loading indicator&lt;/h2&gt;
&lt;p&gt;A new indicator has been added to let users know JupyterLite is currently starting up, which is useful for deployments that take some time to load, especially on slow connections.&lt;/p&gt;
&lt;p&gt;By default, the loading indicator is only visible in the JupyterLab application (not for Jupyter Notebook or REPL). If you would like to enable or disable the indicator for some or all applications, check out the &lt;a href="https://jupyterlite.readthedocs.io/en/latest/howto/configure/loading_indicator.html"&gt;guide in the documentation&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The loading indicator lets users know something is happening when the page is loaded" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/005-1_4ExdD0fKhHbnaVM5XjbUcg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The loading indicator lets users know something is happening when the page is loaded&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="settings-import-and-export"&gt;Settings import and export&lt;/h2&gt;
&lt;p&gt;Settings can now be exported to an &lt;code&gt;overrides.json&lt;/code&gt; file from the Settings Editor. This file can then be used to pre-configure defaults in deployments or to restore settings.&lt;/p&gt;
&lt;p&gt;This allows users to interact with a JupyterLite deployment, make a couple of changes to the settings, and export them to an &lt;code&gt;overrides.json&lt;/code&gt; that can be then included in a JupyterLite deployment, or imported in another one.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The new export and import buttons in the Settings Editor" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/006-0_dAPLUZFcDVPUKUeu.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The new export and import buttons in the Settings Editor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="plugin-manager"&gt;Plugin Manager&lt;/h2&gt;
&lt;p&gt;Open the command palette and search for &lt;code&gt;Advanced Plugin Manager&lt;/code&gt; to open the plugin manager. With the plugin manager, users can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View active plugins in the running JupyterLite environment&lt;/li&gt;
&lt;li&gt;Understand dependency relationships between plugins&lt;/li&gt;
&lt;li&gt;Gain better visibility into the configuration of their environment&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;img alt="The plugin manager lists active plugins in a JupyterLite deployment" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/007-0_HRQIND8DW4sYlvzy.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The plugin manager lists active plugins in a JupyterLite deployment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="improved-multi-site-contents-management"&gt;Improved Multi-site Contents Management&lt;/h2&gt;
&lt;p&gt;The default in-browser storage is now scoped using the &lt;code&gt;baseUrl&lt;/code&gt; of the deployment. This allows hosting multiple sites under the same domain, and keeps user created files isolated from each other.&lt;/p&gt;
&lt;p&gt;For example if two sites are hosted on GitHub Pages and available under the following URLs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://username.github.io/jupyterlite-foo"&gt;&lt;code&gt;https://username.github.io/jupyterlite-foo&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://username.github.io/jupyterlite-bar"&gt;&lt;code&gt;https://username.github.io/jupyterlite-bar&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practice this fixes the issue where users would create new files in one site, and they would show up in another site hosted under the same domain.&lt;/p&gt;
&lt;h2 id="multi-tab-file-system-access"&gt;Multi-Tab File System Access&lt;/h2&gt;
&lt;p&gt;Using JupyterLite in multiple browser tabs at the same time should now result in less issues, in particular when interacting with the file system.&lt;/p&gt;
&lt;h2 id="extension-system"&gt;Extension system&lt;/h2&gt;
&lt;p&gt;In previous versions, JupyterLite was creating a separate application to register “server” extensions. Examples of such extensions are the different managers (sessions, kernels, settings, contents) but also the actual kernels (Pyodide, Xeus Python). This was not ideal because it introduced a separate namespace for these extensions, and extension authors had to configure their extension to be either a regular JupyterLab extension or a JupyterLite “server” extension.&lt;/p&gt;
&lt;p&gt;Starting with JupyterLite 0.6.0, all extensions are now registered at the same level as regular JupyterLab extensions, making use of the new &lt;code&gt;ServiceManagerPlugin&lt;/code&gt; type of extension introduced in JupyterLab 4.4.&lt;/p&gt;
&lt;h2 id="jupyterlab-44-and-notebook-74"&gt;JupyterLab 4.4 and Notebook 7.4&lt;/h2&gt;
&lt;p&gt;JupyterLite 0.6.0 is built on top of JupyterLab 4.4 and Notebook 7.4, and brings many of the respective improvements and bug fixes. Check out the &lt;a href="/posts/2025/jupyterlab-4-4-and-notebook-7-4-are-available/"&gt;announcement blog post&lt;/a&gt; to learn more.&lt;/p&gt;
&lt;h2 id="kernel-and-other-updates"&gt;Kernel and other updates&lt;/h2&gt;
&lt;p&gt;In addition to the JupyterLite 0.6.0 release, the &lt;a href="https://github.com/jupyterlite/pyodide-kernel"&gt;Pyodide&lt;/a&gt; and &lt;a href="https://github.com/jupyterlite/xeus"&gt;Xeus Python&lt;/a&gt; kernels have also been updated to be compatible with the latest release.&lt;/p&gt;
&lt;p&gt;Other components of the stack such as &lt;a href="/posts/2023/and-voici/"&gt;Voici&lt;/a&gt; and &lt;a href="https://github.com/jupyter-book/thebe"&gt;thebe-lite&lt;/a&gt; will also soon be updated.&lt;/p&gt;
&lt;h2 id="closing-the-gap-with-jupyterlab"&gt;Closing the gap with JupyterLab&lt;/h2&gt;
&lt;p&gt;As mentioned above, JupyterLab 4.4 introduced the ability to replace some of the core &lt;code&gt;ServiceManager&lt;/code&gt; plugins responsible for interacting with the server, such as the kernel and session managers.&lt;/p&gt;
&lt;p&gt;This allows JupyterLite to progressively become a set of JupyterLab extensions that can be integrated in regular JupyterLab instances.&lt;/p&gt;
&lt;h3 id="using-in-browser-and-regular-kernels-together"&gt;Using in-browser and regular kernels together&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;⚠️ This is still very experimental!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;An example of such integration is the &lt;a href="https://github.com/jupyterlab-contrib/jupyterlab-hybrid-kernels"&gt;jupyterlab-hybrid-kernels&lt;/a&gt; extension.&lt;/p&gt;
&lt;p&gt;This extension allows using both in-browser (JupyterLite) and regular Jupyter kernels together in the same JupyterLab session.&lt;/p&gt;
&lt;p&gt;There is also some work happening to explore automatically promoting in-browser kernels as regular kernels when some computations are not possible in the browser, or when some modules are not available.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Promoting in-browser as regular kernels" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/008-1_cjJGJmrDUisWFcVejvqytg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Promoting in-browser as regular kernels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="migrating-to-060"&gt;Migrating to 0.6.0&lt;/h2&gt;
&lt;p&gt;If you maintain a JupyterLite deployment and would like to update to the latest version, check out the &lt;a href="https://jupyterlite.readthedocs.io/en/latest/migration.html#to-0-6-0"&gt;migration guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="walkthrough-video"&gt;Walkthrough video&lt;/h2&gt;
&lt;p&gt;For a more interactive tour of the new features, watch the video below:&lt;/p&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/jkQ9ORG5qAM" title="What&amp;#x27;s new in JupyterLite 0.6 💡" width="560" height="315" style="aspect-ratio: 560 / 315" loading="lazy" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
&lt;h2 id="try-it-in-your-browser"&gt;Try it in your browser&lt;/h2&gt;
&lt;p&gt;If you would like to try the new features in your browser, click on the following link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyter.org/try-jupyter/lab?path=notebooks%2FIntro.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/009-1_mDE6gcWgCV_izR-LxNdf5g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;The work on JupyterLite at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; was contributed by &lt;strong&gt;Anastasia Sliusar&lt;/strong&gt;, &lt;strong&gt;Ian Thomas&lt;/strong&gt;, &lt;strong&gt;Jeremy Tuloup&lt;/strong&gt;, &lt;strong&gt;Martin Renou&lt;/strong&gt; and &lt;strong&gt;Thorsten Beier&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="https://x.com/TechAtBloomberg"&gt;Bloomberg&lt;/a&gt; for funding this work and for their ongoing support for JupyterLite in general.&lt;/p&gt;
&lt;p&gt;Thanks to the &lt;a href="https://www.gatesfoundation.org/"&gt;&lt;strong&gt;Bill &amp;amp; Melinda Gates Foundation&lt;/strong&gt;&lt;/a&gt; (through a grant to &lt;strong&gt;CourseKata&lt;/strong&gt;) for funding the work on the interactive input feature.&lt;/p&gt;
&lt;p&gt;Many thanks to all the contributors to JupyterLite, Jupyter Frontends and related project in the Jupyter ecosystem (list below is based on GitHub usernames):&lt;/p&gt;
&lt;p&gt;@2xB | @anutosh491 | @bollwyvl | @davidbrochart | @ianthomas23 | @jtpio | @juntyr | @lesteve | @martinRenou | @RMattB1 | @trungleduc | @westurner | @yogabonito&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/jupyterlite-0-6-0-is-released/images/010-1_ELbE4YunBEU1TtlXT0jxuA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLite"/><category term="releases"/></entry><entry><title>R in the Browser: Announcing Our WebAssembly Distribution</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/" rel="alternate"/><published>2025-02-28T08:31:00+00:00</published><updated>2025-04-25T07:39:00+00:00</updated><author><name>Isabel Paredes</name></author><id>tag:jasongrout.github.io,2025-02-28:/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/</id><summary type="html">&lt;p&gt;R is now available in emscripten-forge, enabling the Xeus-R kernel in JupyterLite&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/001-1_y4YmV8C0L1CkE1Q5JRJLkg.webp" alt="Banner with gradient background and R-lite logo" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;In the past few months, &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; has received funding from the &lt;a href="https://www.gatesfoundation.org/"&gt;&lt;strong&gt;Bill &amp;amp; Melinda Gates Foundation&lt;/strong&gt;&lt;/a&gt; through a grant to &lt;a href="https://coursekata.org/"&gt;CourseKata&lt;/a&gt; to develop support for the R programming language in &lt;a href="https://jupyterlite.readthedocs.io/en/stable/"&gt;JupyterLite&lt;/a&gt; and the &lt;a href="https://emscripten-forge.org/"&gt;Emscripten-forge&lt;/a&gt; distribution for WebAssembly.&lt;/p&gt;
&lt;p&gt;Today, we are thrilled to announce the availability of the R programming language in the Emscripten-Forge distribution for WebAssembly, which enables R workflows in JupyterLite through the Xeus-R Jupyter kernel.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screenshot of JupyterLite with an R Jupyter notebook in use" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/002-0_9D50aOr7jhxFnjzo.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLite with an R Jupyter notebook in use&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To experience R in JupyterLite, simply click on the link below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://isabelparedes.github.io/xeus-r-demo/lab/?path=demo.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/003-0_qOr5KTmon-fiZ37l.webp" alt="Button to access JupyterLite" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this article, we present the details of our efforts to enable R workflows in JupyterLite. We cover the challenges encountered in packaging R and its dependencies for this platform, the solutions we implemented, and the process of handling R mamba packages on the frontend.&lt;/p&gt;
&lt;h2 id="why-webassembly-scaling-scientific-computing-workflows-in-jupyter-to-millions-of-users"&gt;&lt;strong&gt;Why WebAssembly? Scaling scientific computing workflows in Jupyter to millions of users&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The main challenge in scaling Jupyter deployments lies in providing a live computing environment on the server side for each user session.&lt;/p&gt;
&lt;p&gt;While Kubernetes-based JupyterHub deployments have been successfully implemented at a university scale (such as the UC Berkeley Data 8 class, or the JupyterHub deployment of Université Paris Saclay), they demand substantial computing resources and a skilled DevOps team to achieve and maintain this level of scalability.&lt;/p&gt;
&lt;p&gt;In contrast, &lt;a href="https://jupyterlite.readthedocs.io/"&gt;&lt;strong&gt;JupyterLite&lt;/strong&gt;&lt;/a&gt; represents a fundamentally different paradigm. As a browser-based distribution of Jupyter, it runs language kernels directly in the browser, enabling significantly greater scalability and ease of deployment. Notable JupyterLite-based applications include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A JupyterLite console integrated into &lt;a href="http://numpy.org"&gt;&lt;strong&gt;NumPy&lt;/strong&gt;&lt;/a&gt;’s front page, providing immediate access to NumPy functionality without requiring installation.&lt;/li&gt;
&lt;li&gt;A similar console application on &lt;a href="https://live.sympy.org/"&gt;&lt;strong&gt;SymPy&lt;/strong&gt;&lt;/a&gt;’s project page, fully hosted statically.&lt;/li&gt;
&lt;li&gt;The official &lt;a href="https://jupyter.org/try"&gt;&lt;strong&gt;Jupyter&lt;/strong&gt;&lt;/a&gt; website¹.&lt;/li&gt;
&lt;li&gt;The examples in &lt;a href="https://scikit-learn.org/stable/lite/lab/index.html"&gt;&lt;strong&gt;Scikit-Learn&lt;/strong&gt;&lt;/a&gt;’s documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These examples illustrate how WebAssembly can be transformative by providing ready-to-use computing environments for scientific workflows, in documentation, blog posts, and scientific communications. The WebAssembly environments are made available to the hundreds of thousands of monthly visitors of these websites without requiring any computing resources on the backend.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://NumPy.org"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/004-0_-531naCHla3pK14Q.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://live.sympy.org/"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/005-0_vk6bu54D0C7y4k9b.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://jupyter.org/try"&gt;&lt;img alt="JupyterLite deployments displayed on the NumPy, SymPy, and Jupyter websites" src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/006-0_SKvqpunHcz3i2mBG.webp" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;JupyterLite deployments displayed on the NumPy, SymPy, and Jupyter websites&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;[1] Even though it powers the deployment of Jupyter on the main Jupyter website, JupyterLite is not an officially sanctioned Jupyter project.&lt;/p&gt;
&lt;h2 id="beyond-python-enabling-r-in-the-browser-and-more"&gt;&lt;strong&gt;Beyond Python, enabling R in the browser, and more&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;While the initial focus of the JupyterLite team has been on the Python programming language, the JupyterLite infrastructure was designed to be language-agnostic from the outset. The same principle applies to the package management stack we have been contributing to over the past few years, namely, &lt;a href="https://github.com/mamba-org/mamba"&gt;mamba&lt;/a&gt; and &lt;a href="https://conda-forge.org/"&gt;conda-forge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When we began targeting the WebAssembly platform, we chose to build our efforts on the mamba and conda-forge ecosystem. This decision allowed us to provide customizable environments for WebAssembly and expand beyond Python. This enabled applications such as the &lt;a href="https://github.com/jupyterlite/terminal"&gt;&lt;strong&gt;JupyterLite terminal&lt;/strong&gt;&lt;/a&gt;, created by Ian Thomas, which comprises a bash-like shell written in TypeScript called &lt;a href="https://github.com/jupyterlite/cockle"&gt;Cockle&lt;/a&gt;, and WebAssembly builds of native shell commands such as cat, grep, ls, and vim. You can read the &lt;a href="/posts/2024/jupyterlite-terminal/"&gt;&lt;strong&gt;earlier&lt;/strong&gt; &lt;strong&gt;announcement&lt;/strong&gt;&lt;/a&gt; introducing the JupyterLite terminal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Today, we are excited to reveal the availability of R in emscripten-forge, enabling Jupyter kernels such as Xeus-R to be used in JupyterLite.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With the addition of R, emscripten-forge now provides a unified packaging solution for Python, R, and terminal applications for WebAssembly. This covers the scopes of &lt;a href="https://pyodide.org/"&gt;&lt;strong&gt;Pyodide&lt;/strong&gt;&lt;/a&gt; (a Python distribution for WebAssembly), &lt;a href="https://docs.r-wasm.org/"&gt;&lt;strong&gt;WebR&lt;/strong&gt;&lt;/a&gt; (an R distribution for WebAssembly), and &lt;a href="https://sandbox.bio/"&gt;&lt;strong&gt;Sandbox.bio&lt;/strong&gt;&lt;/a&gt; (a shell emulator with WebAssembly commands).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="building-r-for-emscripten-forge"&gt;&lt;strong&gt;Building R for Emscripten-forge&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Emscripten-forge is a GitHub organization containing recipes to build &lt;strong&gt;conda packages&lt;/strong&gt; for the emscripten-wasm32 platform; it is also the first distribution of conda packages targeting this platform. It is built upon a modernized stack, leveraging &lt;strong&gt;rattler-build&lt;/strong&gt; (the successor to the conda-build package builder) and &lt;strong&gt;mamba&lt;/strong&gt;. The emscripten-forge project, which was created and is still led by Thorsten Beier, is now a team effort covering a broad range of packages.&lt;/p&gt;
&lt;p&gt;In order to build R with emscripten-forge, we first required a toolchain capable of converting Fortran and C/C++ code into WebAssembly. Since R relies on BLAS and LAPACK (which are written in Fortran) and many essential R packages wrap native libraries that use Fortran, this conversion capability was crucial.&lt;/p&gt;
&lt;h2 id="fortran"&gt;&lt;strong&gt;Fortran&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;By default, the emscripten-forge toolchain can cross-compile any C/C++ packages into WebAssembly; however, the first challenge in building the R stack was expanding the toolchain to also compile Fortran code. The two compilers that we considered for this task are LFortran and Flang.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lfortran.org/"&gt;&lt;strong&gt;LFortran&lt;/strong&gt;&lt;/a&gt; is a compiler built on top of LLVM and designed from the ground up as a cross-compiler. Despite its potential, LFortran is still in its early stages compared to Flang and does not yet fully support all Fortran features.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flang.llvm.org/"&gt;&lt;strong&gt;LLVM Flang&lt;/strong&gt;&lt;/a&gt; is the Fortran compiler of the LLVM project. Unfortunately, it does not support cross-compilation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Working with Serge Guelton and Kerim Birgi, we initially experimented with the LFortran compiler and contributed to addressing issues encountered when building Netlib &lt;a href="https://github.com/Reference-LAPACK/lapack"&gt;LAPACK&lt;/a&gt;. The BLAS implementation from this package served as a valuable testing ground since BLAS is one of the core dependencies for R.&lt;/p&gt;
&lt;p&gt;After careful consideration, we found that Flang, with its ability to compile a wider variety of Fortran projects, brought us closer to building the Fortran components of R to WebAssembly. George Stagg, the main developer of the WebR project, has written a detailed &lt;a href="https://gws.phd/posts/fortran_wasm/"&gt;article&lt;/a&gt; on using Flang to produce WebAssembly, which was adapted to the (then) latest version of LLVM (&lt;a href="https://github.com/r-wasm/llvm-project/blob/wasm/llvm/CMakeLists.txt"&gt;v18&lt;/a&gt;). Inspired by George Stagg’s endeavors, Serge posted upstream contributions to the Flang project (&lt;a href="https://github.com/llvm/llvm-project/pull/99465"&gt;#99465&lt;/a&gt;, &lt;a href="https://github.com/llvm/llvm-project/pull/99822"&gt;#99822&lt;/a&gt;, &lt;a href="https://github.com/llvm/llvm-project/pull/101242"&gt;#101242&lt;/a&gt;, &lt;a href="https://github.com/llvm/llvm-project/pull/105589"&gt;#105589&lt;/a&gt;), resolving issues with 32-bit platforms that were impacting our effort to support WebAssembly. All of these changes will be included in LLVM/Flang 20.0.&lt;/p&gt;
&lt;p&gt;The remaining patches which cannot be merged upstream at this point involve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) enabling code generation that targets WebAssembly (&lt;a href="https://github.com/llvm/llvm-project/commit/198c0e1e23a52abb3e274ee44223551247f2c38f"&gt;198c0e1&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;(2) encoding target-specific size definitions for various data types given that the host and target platforms have different architectures (&lt;a href="https://github.com/llvm/llvm-project/commit/1da66cda3652da369f5d1be871c27368a12b55e9"&gt;1da66cd&lt;/a&gt;), and&lt;/li&gt;
&lt;li&gt;(3) disabling support for 128-bit floating-point types (&lt;a href="https://github.com/llvm/llvm-project/commit/f20c7c05db0ed9afb50c0092dad7bc991d933481"&gt;f20c7c0&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A version of (2) that could be contributed upstream is in the works, which would be one step closer to making WebAssembly an official target for Flang. Additionally, (3) will no longer be needed starting with Flang v20.&lt;/p&gt;
&lt;p&gt;These patches were integrated by Axel Obermeier into the Flang build recipe for the conda-forge distribution, making the modified build accessible to everyone. It can be installed on Linux with mamba or micromamba by executing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;micromamba&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;conda-forge/label/emscripten::flang&lt;span class="w"&gt; &lt;/span&gt;libllvm19&lt;span class="w"&gt; &lt;/span&gt;--no-channel-priority
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The final ingredient for our toolchain involved creating a compatible Fortran runtime library. Since Flang implements its runtime library in C/C++, compiling this library with Emscripten proved to be straightforward. We added a &lt;a href="https://github.com/emscripten-forge/recipes/blob/9b08a19a531bb5e03c7d5147367f6de2acb57e40/recipes/recipes_emscripten/libflang/recipe.yaml"&gt;recipe for libflang&lt;/a&gt; to emscripten-forge, which packages the runtime library and includes all the aforementioned patches.&lt;/p&gt;
&lt;p&gt;In the following example, we invoke Flang on a simple Fortran script, and execute the resulting JavaScript and WebAssembly with NodeJS.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;ls
hello.f90&lt;span class="w"&gt;  &lt;/span&gt;libFortranRuntime.a
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;hello.f90
File:&lt;span class="w"&gt; &lt;/span&gt;hello.f90
program&lt;span class="w"&gt; &lt;/span&gt;hello
&lt;span class="w"&gt; &lt;/span&gt;print&lt;span class="w"&gt; &lt;/span&gt;*,&lt;span class="w"&gt; &lt;/span&gt;“Hello,&lt;span class="w"&gt; &lt;/span&gt;Fortran!”
end&lt;span class="w"&gt; &lt;/span&gt;program&lt;span class="w"&gt; &lt;/span&gt;hello
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/emsdk/emsdk_env.sh
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;flang-new&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="o"&gt;=&lt;/span&gt;wasm32-unknown-emscripten&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;hello.f90&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;hello.o
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;emcc&lt;span class="w"&gt; &lt;/span&gt;hello.o&lt;span class="w"&gt; &lt;/span&gt;libFortranRuntime.a&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;hello.js
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;ls
hello.f90&lt;span class="w"&gt;  &lt;/span&gt;hello.js&lt;span class="w"&gt;  &lt;/span&gt;hello.o&lt;span class="w"&gt;  &lt;/span&gt;hello.wasm&lt;span class="w"&gt;  &lt;/span&gt;libFortranRuntime.a
&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;node&lt;span class="w"&gt; &lt;/span&gt;hello.js
Hello,&lt;span class="w"&gt; &lt;/span&gt;Fortran!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With a working Fortran-to-WebAssembly toolchain, we managed to compile BLAS and LAPACK (See the &lt;a href="https://github.com/emscripten-forge/recipes/blob/main/recipes/recipes_emscripten/lapack/recipe.yaml"&gt;LAPACK recipe&lt;/a&gt;). But before we could get started with building R, we needed to tackle the remaining R dependencies.&lt;/p&gt;
&lt;h2 id="walking-up-the-stack"&gt;&lt;strong&gt;Walking up the stack&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In R (v4.4), there are a handful of dependencies that are required to provide basic functionality; these are: libiconv, zlib, bzip2, xz, pcre2, and libcurl. Thanks to the packaging efforts led by Thorsten Beier and Wolf Vollprecht, these dependencies are available on emscripten-forge with the exception of libcurl. Because of its socket-based networking architecture and reliance on system-level resource access, libcurl faces fundamental incompatibilities with WebAssembly’s sandboxed environment constraints. Therefore, libcurl had to be disabled in the R source code. As a consequence, downstream R packages which depend on libcurl cannot be ported to WebAssembly until a suitable libcurl replacement is available.&lt;/p&gt;
&lt;p&gt;Apart from the core dependencies, R also requires a BLAS implementation. Although external BLAS libraries can be used with R, the R code base contains linear algebra routines from Netlib BLAS and a subset of LAPACK where both libraries are written in C and Fortran. To simplify configuration, we opted for these internal implementations instead of using external BLAS and LAPACK libraries.&lt;/p&gt;
&lt;p&gt;Furthermore, several key optional dependencies were cross-compiled to WebAssembly to enhance R’s graphical capabilities; these include libpng, libtiff, cairo, and pango, along with their respective subdependencies. One particularly challenging aspect of packaging these libraries was the cross-compilation of glib, which both cairo and pango depend on. Compiling glib was only possible thanks to the efforts of &lt;a href="https://gist.github.com/kleisauke/acfa1c09522705efa5eb0541d2d00887"&gt;Kleis Auke Wolthuizen&lt;/a&gt; whose patches made glib compatible with Emscripten, and Johan Mabille who disabled multi-threading.&lt;/p&gt;
&lt;p&gt;The R dependencies are summarized in the following table:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A table summarizing the R dependencies." src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/r-in-the-browser-announcing-our-webassembly/images/007-1_9VQSZHRow-lBASg0wxfklg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Table of R dependencies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="cross-compilation-of-r"&gt;&lt;strong&gt;Cross-compilation of R&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Although R typically configures and builds seamlessly on most unix platforms, cross-compiling is not straightforward because R uses a bootstrap approach; first it creates a minimal version of R which is then used to run several R scripts to complete the build process. Bootstrapping is a challenge because the built binaries for the target platform (WebAssembly) are not executable (at least not directly) from the host platform. Therefore, the r-base package necessitates two build phases. First, we compile R for the host platform (Linux) with GCC and Flang; this generates the R and Rscript executables needed for the second phase. And then, we cross-compile to WebAssembly with Emscripten and Flang.&lt;/p&gt;
&lt;p&gt;One of the major challenges of this setup was generating the R Data Base (.rdb) and R Data Index (.rdx) files for the internal packages of r-base (base, compiler, grDevices, graphics, grid, methods, parallel, splines, stats, stats4, tools, and utils). To accomplish this, it was necessary to dynamically load some of the internal shared libraries (tools, grDevices, graphics, utils, stats, and methods) which provide essential functionality to generate the data and index files. However, the R executables we created during the first phase of the build are incompatible with the WebAssembly shared libraries. In order to work around this, we temporarily replaced the WebAssembly libraries of the internal packages with the linux shared libraries we built during the first phase (See &lt;a href="https://github.com/emscripten-forge/recipes/blob/9b08a19a531bb5e03c7d5147367f6de2acb57e40/recipes/recipes_emscripten/r-base/cross_libraries.sh"&gt;cross_libraries.sh&lt;/a&gt;). This makeshift solution is also used to cross-compile all other R packages; the setup is carried out by the activation script of the cross-r-base package (See &lt;a href="https://github.com/conda-forge/r-base-feedstock/blob/54c15117f86693d54d69aacdd2fb76f622e31e5a/recipe/activate-cross-r-base.sh#L18"&gt;activate-cross-r-base.sh&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Additionally, the R source code required a few modifications to enable cross-compilation to WebAssembly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Emscripten platform was added to select the correct configuration for the platform.&lt;/li&gt;
&lt;li&gt;The dependency on libcurl was removed (as mentioned in the previous section) and as a consequence, the internet package which relies on libcurl was disabled.&lt;/li&gt;
&lt;li&gt;System calls which are not compatible with our WebAssembly environment were disabled.&lt;/li&gt;
&lt;li&gt;Cairo replaced Xlib as the default bitmap type to enable graphics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete list of patches applied to R is available here: &lt;a href="https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten/r-base/patches"&gt;r-base patches&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="the-r-package-ecosystem"&gt;&lt;strong&gt;The R package ecosystem&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Building R itself was just the beginning. The next challenge was to provide a wide array of R packages to create a robust development environment. Fortunately, many packages are written in pure R and are available as “noarch” packages on conda-forge, making them directly compatible with emscripten-forge environments. However, for the R packages that require compilation, a package recipe needs to be created and added to emscripten-forge. Thanks to the help of Anutosh Bhat, the following packages have been compiled and are available to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;r-askpass     r-digest    r-haven      r-magrittr  r-purrr    r-tidyr
r-base64enc   r-dplyr     r-hexbin     r-mass      r-rcpp     r-tzdb
r-bit         r-ellipsis  r-htmltools  r-matrix    r-readr    r-utf8
r-bit64       r-fansi     r-isoband    r-mgcv      r-rlang    r-vctrs
r-cachem      r-farver    r-jsonlite   r-mime      r-sp       r-vroom
r-cli         r-fastmap   r-later      r-nlme      r-stringi  r-xfun
r-colorspace  r-ggrepel   r-lattice    r-plyr      r-sys      r-yaml
r-data.table  r-glue      r-lazyeval   r-promises  r-tibble
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most of the time, cross-compiling an R package is very simple because the bulk of the work is handled by r-base. Building a package typically boils down to a single line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;$R&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CMD&lt;span class="w"&gt; &lt;/span&gt;INSTALL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$R_ARGS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Occasionally, minor patches are applied to the package source code to enable the WebAssembly target platform or to simplify cross-compilation. Some R packages, such as r-nlme, contain Fortran code; for such packages, the custom flang compiler needs to be installed before the package can be cross-compiled. Should any package be needed for your use case, feel free to contribute them to emscripten-forge!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="xeus-r"&gt;&lt;strong&gt;Xeus-R&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Once a sufficient set of packages is available for the platform, the next step is to provide a development environment for end users. Our chosen tool for this purpose is Jupyter.&lt;/p&gt;
&lt;p&gt;The most popular Jupyter kernel for R is &lt;a href="https://github.com/IRkernel/IRkernel"&gt;IRkernel&lt;/a&gt;, which has been developed over several years. It includes complete ZeroMQ bindings for the R language and comprehensive coverage of the Jupyter protocol. However, since IRkernel is tied to ZeroMQ, we opted to work with the &lt;a href="https://github.com/jupyter-xeus/xeus-r"&gt;Xeus-R&lt;/a&gt; project instead, which was &lt;a href="/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/"&gt;announced last year.&lt;/a&gt; Xeus-R was developed by Romain François in collaboration with our team and with JupyterLite in mind. It is built upon the Xeus library, which provides a native implementation of the Jupyter protocol and is adapted to work with the JupyterLite frontend.&lt;/p&gt;
&lt;p&gt;Fortunately, the components of IRkernel responsible for the rich representation of R objects and rich display in Jupyter frontends were split into two separate R packages: &lt;strong&gt;IRdisplay&lt;/strong&gt; and &lt;strong&gt;repr&lt;/strong&gt;. These packages can be reused in Xeus-R. As a result, Xeus-R and IRkernel offer the same rich display functionality, ensuring that Jupyter notebooks created with one kernel will work with the other. However, Xeus-R provides a different implementation of the communication layer through &lt;a href="https://github.com/jupyterlite/xeus"&gt;jupyterlite-xeus&lt;/a&gt;, which enables integration with the JupyterLite stack. This integration was previously implemented for Xeus-Python and Xeus-Lua.&lt;/p&gt;
&lt;p&gt;One key aspect of this integration is the populating of the in-memory file system used by JupyterLite kernels with the required runtime dependencies, particularly shared libraries. This process is done upon bootstrapping the kernel. For this purpose, Anastiasiia Sliusar and Martin Renou developed &lt;a href="https://github.com/emscripten-forge/mambajs"&gt;mambajs&lt;/a&gt;, which, in combination with jupyterlite-xeus, processes conda packages in the frontend and installs them in the kernel filesystem at startup. In WebAssembly, shared libraries need to be handled with special care in order to function as expected. Drawing inspiration from the Python community, we adapted the pioneering work of Pyodide to properly load shared libraries into the filesystem.&lt;/p&gt;
&lt;h2 id="how-to-make-a-deployment-with-custom-packages"&gt;&lt;strong&gt;How to make a deployment with custom packages&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The GitHub repository &lt;a href="https://github.com/jupyterlite/xeus-lite-demo"&gt;https://github.com/jupyterlite/xeus-lite-demo&lt;/a&gt; is a template for creating a JupyterLite deployment on GitHub pages that includes the packages specified in a conda environment.&lt;/p&gt;
&lt;p&gt;The process is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new repository from the GitHub template.&lt;/li&gt;
&lt;li&gt;Enable the deployment on GitHub pages from a GitHub action, as shown in the README.&lt;/li&gt;
&lt;li&gt;Edit the environment file to include the desired packages.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, to deploy an R kernel with coursekata installed, the environment.yml file would contain the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;xeus-r&lt;/span&gt;
&lt;span class="nt"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;https://repo.mamba.pm/emscripten-forge&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;conda-forge&lt;/span&gt;
&lt;span class="nt"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;xeus-r&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;r-coursekata&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The deployment linked at the beginning of this article was created with this template.&lt;/p&gt;
&lt;h2 id="how-to-contribute"&gt;&lt;strong&gt;How to contribute&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Contributors are always welcome! New R packages can be requested by opening an issue on emscripten-forge, or added directly by opening a pull request with the desired package recipe. For instructions on how to contribute, please visit &lt;a href="https://emscripten-forge.org/"&gt;https://emscripten-forge.org/&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-is-in-the-works"&gt;&lt;strong&gt;What is in the works&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Currently, we are developing the infrastructure to dynamically install emscripten-forge and conda-forge noarch packages in JupyterLite kernel environments. This will apply to all Xeus kernels that make use of these packages, such as Xeus-Python, Xeus-R, and Xeus-Lua. In the case of R, this may be directly bound to install.packages.&lt;/p&gt;
&lt;p&gt;Additionally, we are working on more advanced features for the R kernel, including support for Jupyter interactive widgets and the split of the pure R parts of xeus-r into a separate package, which would facilitate a possible convergence with IRkernel.&lt;/p&gt;
&lt;p&gt;Finally, other language kernels based on the same package management stack are also in the works. Stay tuned for future announcements!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="about-the-author"&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Isabel Paredes&lt;/strong&gt;, who led the charge on bringing R to emscripten-forge, is a senior scientific software developer at QuantStack. Prior to working on this project, she focused on porting the Robot Operating System (ROS) framework to WebAssembly.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;&lt;strong&gt;Acknowledgments&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;This effort brought together several endeavors from many open-source developers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JupyterLite&lt;/strong&gt;, the Jupyter distribution that runs entirely in the web browser, was created by Jeremy Tuloup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Xeus&lt;/strong&gt;, the C++ library implementing the Jupyter kernel protocol, enabling a custom communication layer, and is foundational to kernels like xeus-r, xeus-python, running in JupyterLite, was created by Johan Mabille and is maintained by a broader team including Martin Renou, Sylvain Corlay, and Thorsten Beier, who worked on the first integration with JupyterLite.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emscripten-forge&lt;/strong&gt;, the distribution of conda packages for WebAssembly, was created by Thorsten Beier, who continues to lead the project. Many recipes were contributed by Isabel Paredes (for the R ecosystem), but also Kerim Birgi, Anutosh Bhat, Martin Renou, Wolf Vollprecht, and Johan Mabille.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Xeus-R&lt;/strong&gt;, the Xeus-based Jupyter kernel for R, was created by Romain François.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IRDisplay&lt;/strong&gt; and &lt;strong&gt;repr&lt;/strong&gt; are pure R packages providing rich display and rich mime type rendering of many core R types, which are used in xeus-r. These two packages stem from the IRkernel project, a Jupyter kernel for R written in R, which was started by Thomas Kluyver, and has been maintained over the years by Philipp Angerer and Jan Katins.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MambaJS&lt;/strong&gt;, the library enabling the processing of conda packages in the frontend, was developed by Anastasiia Sliusar and Martin Renou.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conda-forge&lt;/strong&gt;, the main distribution of conda packages, includes a large number of “noarch” recipes, including for R packages, and is developed by a broad community of contributors worldwide.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebR&lt;/strong&gt;, an R distribution for WebAssembly, was created by George Stagg. Although not directly used in this project, it documented numerous patches and changes to the Flang compiler that were adapted for this effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="funding"&gt;&lt;strong&gt;Funding&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The work by &lt;strong&gt;Isabel Paredes&lt;/strong&gt; and other &lt;strong&gt;QuantStack&lt;/strong&gt; team members on bringing R and Xeus-R to Emscripten-Forge was supported by the &lt;a href="https://www.gatesfoundation.org/"&gt;&lt;strong&gt;Bill &amp;amp; Melinda Gates Foundation&lt;/strong&gt;&lt;/a&gt; through a grant to &lt;strong&gt;CourseKata&lt;/strong&gt;. CourseKata is a nonprofit project dedicated to improving statistics and data science education through interactive, research-backed curricula that integrate professional tools like R and Jupyter notebooks. This grant was part of CourseKata’s broader mission to scale its innovative curriculum, equipping students with the skills and confidence to engage with statistics, data science, and coding in meaningful ways.&lt;/p&gt;
</content><category term="JupyterLite"/><category term="kernels"/><category term="WebAssembly"/><category term="xeus"/></entry><entry><title>JupyterLite Terminal</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/" rel="alternate"/><published>2024-11-13T19:37:00+00:00</published><updated>2024-11-13T19:37:00+00:00</updated><author><name>Ian Thomas</name></author><id>tag:jasongrout.github.io,2024-11-13:/medium-archive/pelican/posts/2024/jupyterlite-terminal/</id><summary type="html">&lt;p&gt;JupyterLite is an unofficial Jupyter distribution that runs entirely in the web browser without any server components. Deployment and…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/001-1_OSnVBpfV12C1YpgVnuXOyw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterlite.readthedocs.io/en/stable/"&gt;JupyterLite&lt;/a&gt; is an unofficial Jupyter distribution that runs entirely in the web browser without any server components. Deployment and hosting are easy as JupyterLite sites are just static websites.&lt;/p&gt;
&lt;p&gt;JupyterLite isn’t yet as fully-featured as the more established JupyterLab and developers are always looking for ways to add new features to bridge that gap. &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; are therefore delighted to announce that we have been working on a new Terminal for JupyterLite.&lt;/p&gt;
&lt;h2 id="how-do-you-try-out-the-jupyterlite-terminal"&gt;How do you try out the JupyterLite Terminal?&lt;/h2&gt;
&lt;p&gt;Go to &lt;a href="https://jupyterlite-terminal.vercel.app/"&gt;https://jupyterlite-terminal.vercel.app/&lt;/a&gt; and click on the Terminal launcher.&lt;/p&gt;
&lt;h2 id="what-can-you-do-with-the-jupyterlite-terminal"&gt;What can you do with the JupyterLite Terminal?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Run shell commands such as &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; on the same file system as the rest of JupyterLite:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/002-1_dkyeiiagy9zZnfCxjkCgPg.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use tab completion of command and file names, and pipes between commands:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/003-1_wTGFO6IDHQ0YSYjso-XUtA.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store and use the command history:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/004-1_qFCSqPLAJptGXf4fCC7F0g.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the theme settings:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/005-1_UxMOxMnDiheATrA044BK5A.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can even run &lt;code&gt;lua&lt;/code&gt; scripts from the command line:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/006-1_LbyGHamQs-_pNmk2vmDdfQ.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To list the available commands press &lt;code&gt;tab&lt;/code&gt; in an empty prompt:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/007-1__qX0_9_LgO1r9Is5Oj7u5Q.webp" alt="List of supported commands" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="how-does-it-work"&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;In JupyterLab the Terminal connects to a real shell such as &lt;code&gt;bash&lt;/code&gt; running on the server. In JupyterLite the Terminal runs entirely within the browser with commands that are compiled to WebAssembly and glued together with TypeScript to provide &lt;code&gt;bash&lt;/code&gt;-like functionality.&lt;/p&gt;
&lt;p&gt;The WebAssembly commands are compiled on &lt;a href="https://emscripten-forge.org/"&gt;Emscripten-forge&lt;/a&gt; and automatically packaged up when you build a JupyterLite deployment using the Terminal.&lt;/p&gt;
&lt;p&gt;You can see which Emscripten-forge packages are included in your Terminal using the &lt;code&gt;cockle-config&lt;/code&gt; command:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/jupyterlite-terminal/images/008-1_5xqYgXmsS6dJsonJCBU23g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="what-is-the-status-of-the-project"&gt;What is the status of the project?&lt;/h2&gt;
&lt;p&gt;The JupyterLite Terminal is still experimental and there are lots of things it doesn’t yet do. But it is under active development and major improvements are planned for the coming months. These will include more commands, improved&lt;code&gt;bash&lt;/code&gt;-like shell functionality, and instructions on how to build and use your own WebAssembly commands.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jupyterlite-terminal.vercel.app/"&gt;JupyterLite deployment&lt;/a&gt; to try out the Terminal.&lt;/p&gt;
&lt;p&gt;Github repositories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlite/terminal"&gt;terminal&lt;/a&gt;: JupyterLite extension for the Terminal.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlite/cockle"&gt;cockle&lt;/a&gt;: underlying &lt;code&gt;bash&lt;/code&gt;-like shell functionality.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/emscripten-forge/recipes"&gt;recipes&lt;/a&gt;: Emscripten-forge recipes for WebAssembly commands.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;This work was performed by &lt;strong&gt;Ian Thomas&lt;/strong&gt;, &lt;strong&gt;Jeremy Tuloup&lt;/strong&gt; and &lt;strong&gt;Thorsten Beier&lt;/strong&gt; at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;. Thanks to &lt;a href="https://x.com/TechAtBloomberg"&gt;Bloomberg&lt;/a&gt; for funding this work and for their ongoing support for JupyterLite in general.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ian Thomas&lt;/strong&gt; is a Scientific Software Developer at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;, is a core maintainer of the Open Source projects &lt;a href="https://github.com/matplotlib/matplotlib"&gt;Matplotlib&lt;/a&gt;, &lt;a href="https://github.com/bokeh/bokeh"&gt;Bokeh&lt;/a&gt; and &lt;a href="https://github.com/contourpy/contourpy"&gt;ContourPy&lt;/a&gt;, and has an interest in many parts of the Jupyter ecosystem.&lt;/p&gt;
</content><category term="JupyterLite"/></entry><entry><title>Embed interactive itkwidgets 3D renderings into JupyterLite deployments</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/embed-interactive-itkwidgets-3d-renderings-into/" rel="alternate"/><published>2023-03-10T14:04:00+00:00</published><updated>2023-03-15T10:27:00+00:00</updated><author><name>Matt McCormick @thewtex@fosstodon.org</name></author><id>tag:jasongrout.github.io,2023-03-10:/medium-archive/pelican/posts/2023/embed-interactive-itkwidgets-3d-renderings-into/</id><summary type="html">&lt;p&gt;A tutorial that demonstrates a zero-server, interactive 3D rendering notebook and walks through the quick and easy …&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A tutorial that demonstrates a zero-server, interactive 3D rendering notebook and walks through the quick and easy configuration that can be customized to your needs.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;By: Matt McCormick, Brianna Major, Jeremy Tuloup, Wei Ouyang, Stephen Aylward&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero-install&lt;/strong&gt; web applications have transformed the way we consume and deliver software. Browser-based interfaces facilitate rapid discovery, exploration, and universal access.&lt;/p&gt;
&lt;p&gt;However, for research software engineers (RSEs), developing traditional software stacks for web applications is not only onerous, but those stacks may limit essential future scalability and may be even more onerous to sustain. A RSE must face difficult questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who is going to pay to keep the servers online?&lt;/li&gt;
&lt;li&gt;Who is going to pay to scale the servers for many user or datasets?&lt;/li&gt;
&lt;li&gt;When are you going to find the time to learn and keep up-to-date with all the devops knowledge and skills required?&lt;/li&gt;
&lt;li&gt;Who is going to maintain the system and address security vulnerabilities as they arise?&lt;/li&gt;
&lt;li&gt;How is private data on the server managed and kept secure?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As one of my favorite professors used to say, in cases like this we can look to the advice offered by a wise doctor:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Patient: Oh, Doctor, it hurts badly when I move my knee like this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Doctor: Stop moving your knee like that!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In some cases, components of the traditional web application software stack are necessary, and some of those components are easier, more scalable, and more sustainable than others. However, for many RSE use cases, we now can create useful web applications while avoiding traditional server-related hardships altogether.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will demonstrate how to create a &lt;strong&gt;zero-server&lt;/strong&gt; JupyterLite deployment that embeds interactive 3D renderings into advanced scientific applications, such as for deep learning medical image analysis applications using &lt;a href="https://monai.io"&gt;MONAI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;JupyterLite&lt;/a&gt; is a &lt;a href="https://jupyter.org"&gt;JupyterLab&lt;/a&gt; distribution that runs entirely in the browser built from the ground-up using JupyterLab components and extensions. JupyterLite uses a &lt;a href="https://blog.jupyter.org/webassembly.org"&gt;WebAssembly&lt;/a&gt;-based distribution of scientific Python called &lt;a href="https://pyodide.org/en/stable/"&gt;Pyodide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://itkwidgets.readthedocs.io/"&gt;ITKWidgets&lt;/a&gt; provides interactive widgets to visualize images, point sets, and 3D geometry on the web. ITKWidgets is powered by the same WebAssembly technology. It is built on &lt;a href="https://wasm.itk.org"&gt;ITK-Wasm&lt;/a&gt; and &lt;a href="https://imjoy.io/"&gt;ImJoy&lt;/a&gt;, a hybrid computing platform that communicates via symmetrical transparent remote procedure calls. ImJoy and ITKWidgets support browser-based Pyodide communication along with a number of additional server-client communication transport mechanisms.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will first demonstrate a zero-server, interactive 3D rendering notebook. Then, we walk through the quick and easy configuration that can be customized to your needs. Let’s get started! 🚀&lt;/p&gt;
&lt;h3 id="0-preliminaries"&gt;0. Preliminaries&lt;/h3&gt;
&lt;p&gt;Reproduce the figure below, a rendering of medical imaging volume of an abdominal aortic stent, by &lt;a href="https://jupyterlite-itkwidgets-config-post.netlify.app/lab/index.html?path=Hello3DWorld.ipynb"&gt;running the notebook in your web browser&lt;/a&gt;! After the page has loaded, use the standard &lt;code&gt;Shift+Enter&lt;/code&gt; keys to execute the Jupyter notebook cells.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="a medical imaging volume of an abdominal aortic stent rendered in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/embed-interactive-itkwidgets-3d-renderings-into/images/001-0__zowwA1kefuHdpEX.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;a medical imaging volume of an abdominal aortic stent rendered in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that unlike other Jupyter deployments, the python code runs on your system instead of a server.&lt;/p&gt;
&lt;h3 id="1-create-the-jupyterlite-environment"&gt;1. Create the JupyterLite environment&lt;/h3&gt;
&lt;p&gt;To build our sustainable JupyterLite deployment, we will use &lt;a href="https://github.com/conda-forge/miniforge"&gt;a Python environment&lt;/a&gt; that contains Python packages for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;jupyterlite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imjoy_jupyterlab_extension&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any other JupyterLab federated extensions (JupyterLab 3 extensions) that you want in your JupyterLab deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Create a &lt;em&gt;requirements.txt&lt;/em&gt; file with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;jupyterlite&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="o"&gt;]==&lt;/span&gt;&lt;span class="mf"&gt;0.1.0&lt;/span&gt;&lt;span class="n"&gt;b17&lt;/span&gt;
&lt;span class="n"&gt;imjoy_jupyterlab_extension&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And install the packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python -m pip install -r ./requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See also the related &lt;a href="https://jupyterlite.readthedocs.io/en/latest/howto/configure/simple_extensions.html"&gt;JupyterLite extension addition documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="2-add-itkwidgets-and-other-python-packages"&gt;2. Add itkwidgets and other Python packages&lt;/h3&gt;
&lt;p&gt;Next, we will add &lt;em&gt;itkwidgets&lt;/em&gt;, its dependencies, and other Python packages and their dependencies, that we wish to include into the JupyterLite configuration for deployment. These packages, along with &lt;a href="https://github.com/pyodide/pyodide/tree/main/packages"&gt;the packages available in the Pyodide distribution&lt;/a&gt;, will be available in the deployed site.&lt;/p&gt;
&lt;p&gt;Create a &lt;em&gt;jupyterlite_config.json&lt;/em&gt; file, which specifies the locations of the itkwidgets wheel Python packages. Add other desired packages and their dependencies as follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;PipliteAddon&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;piplite_urls&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://files.pythonhosted.org/packages/4c/ee/56f970ca26375176d3e4885f58471a12d5a6794bcefe8ad0ccb8d7158ca3/itkwasm-1.0b82-py3-none-any.whl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://files.pythonhosted.org/packages/6c/55/c3fc7e2b9671d15f0c0becdcb9fad6c330172988744ad6eaa17b71bace88/imjoy_rpc-0.5.16-py3-none-any.whl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://files.pythonhosted.org/packages/69/d9/5a6c8af2f4b4f49a809ae316ae4c12937d7dfda4e5b2f9e4167df5f15c0e/imjoy_utils-0.1.2-py3-none-any.whl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://files.pythonhosted.org/packages/c9/dc/3504845528418aff0b71f4b622bb0e8e12adec2d8f2c1ba21d695b9ac6e6/itkwidgets-1.0a24-py3-none-any.whl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://files.pythonhosted.org/packages/bb/3e/3667ac685ae83887b874896bcb55584797ba6b52a292df3e4b37736a9610/ngff_zarr-0.1.6-py3-none-any.whl&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find links to these URLs by browsing the package on &lt;a href="https://pypi.org"&gt;PyPI&lt;/a&gt; and copying the link from the &lt;em&gt;Download files&lt;/em&gt; page for a package.&lt;/p&gt;
&lt;p&gt;For packages that do not have a wheel on PyPI, you can provide one locally by placing them in the &lt;em&gt;pypi/&lt;/em&gt; directory of your JupyterLite configuration. For example, if you want to use a local version of itkwidgets instead of the version on PyPi, you could directly add &lt;a href="https://github.com/InsightSoftwareConsortium/itkwidgets/raw/2baa8ec865d4c08a4749cc468579742448e524c7/docs/jupyterlite/pypi/dask_image-2022.9.0-py2.py3-none-any.whl"&gt;this &lt;code&gt;dask-image&lt;/code&gt; wheel&lt;/a&gt; to your &lt;em&gt;pypi/&lt;/em&gt; directory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;pypi/
pypi/dask_image-2022.9.0-py2.py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="3-add-notebooks-and-data"&gt;3. Add notebooks and data&lt;/h3&gt;
&lt;p&gt;Add notebooks and data you would like available in the deployment in the &lt;em&gt;files/&lt;/em&gt; directory. In this tutorial, we will add a &lt;a href="https://github.com/InsightSoftwareConsortium/itkwidgets/raw/2baa8ec865d4c08a4749cc468579742448e524c7/docs/jupyterlite/files/Hello3DWorld.ipynb"&gt;&lt;em&gt;Hello3DWorld.ipynb&lt;/em&gt; notebook&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;files/
files/Hello3DWorld.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In your notebook, install additional packages in the first cell with &lt;code&gt;piplite&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;piplite&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;piplite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;itkwidgets==1.0a24&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="4-build-and-deploy"&gt;4. Build and deploy&lt;/h3&gt;
&lt;p&gt;Build your site with the command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter lite build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Serve the site locally with &lt;code&gt;python -m http.server --directory ./_output&lt;/code&gt; or:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter lite serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The site can be deployed and shared with free static file hosting services such as &lt;a href="https://jupyterlite.readthedocs.io/en/latest/quickstart/deploy.html"&gt;GitHub Pages&lt;/a&gt;, &lt;a href="https://jupyterlite.readthedocs.io/en/latest/howto/deployment/vercel-netlify.html"&gt;Netlify&lt;/a&gt;, or &lt;a href="https://fleek.co/"&gt;Fleek&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="whats-next"&gt;What’s Next&lt;/h3&gt;
&lt;p&gt;In this post, we learned how to create a scalable, sustainable, zero-server Jupyter deployment that uses ITKWidgets for 3D rendering. In subsequent posts, we will discuss how to create simple, zero-server, custom web applications written in Python with &lt;a href="https://pyscript.net/"&gt;PyScript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enjoy ITK!&lt;/strong&gt;&lt;/p&gt;
</content><category term="JupyterLite"/><category term="visualization"/></entry><entry><title>Report on the JupyterLite Community Workshop 💡</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/report-on-the-jupyterlite-community-workshop/" rel="alternate"/><published>2023-01-27T13:06:00+00:00</published><updated>2023-01-27T13:30:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2023-01-27:/medium-archive/pelican/posts/2023/report-on-the-jupyterlite-community-workshop/</id><summary type="html">&lt;p&gt;The JupyterLite Community Workshop, held at the OVHcloud headquarters in Paris, France from December 7th to December 9th, 2022, brought…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The JupyterLite Community Workshop, held at the &lt;a href="https://www.ovhcloud.com"&gt;OVHcloud&lt;/a&gt; headquarters in Paris, France from December 7th to December 9th, 2022, brought together a group of developers, educators, and data scientists to discuss the latest advancements in the Jupyter and Python in the browser ecosystems.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Jupyter Community Workshops are a series of community-organized events that aim to tackle challenging development and design projects, grow the community of contributors, and strengthen collaborations.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/report-on-the-jupyterlite-community-workshop/images/001-1_KA7njvnlfwBrg6Pfg3_nQg.jpeg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;One of the main focuses of the workshop was the &lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;JupyterLite&lt;/a&gt; project, a Jupyter distribution built from the ground-up using JupyterLab components and extensions, allowing for very scalable deployments. It already powers inline consoles and notebooks on the websites of major projects of the PyData ecosystem, such as &lt;a href="https://numpy.org/"&gt;NumPy&lt;/a&gt;, &lt;a href="https://www.sympy.org/en/shell.html"&gt;SymPy&lt;/a&gt;, &lt;a href="https://www.pymc.io"&gt;PyMC&lt;/a&gt;, and many more.&lt;/p&gt;
&lt;p&gt;The workshop also gathered the main developers and contributors of the Pyodide, PyScript and Emscripten-Forge projects that enable using Python in the browser.&lt;/p&gt;
&lt;h2 id="highlights-of-the-week"&gt;Highlights of the week&lt;/h2&gt;
&lt;p&gt;During the workshop, attendees had the opportunity to participate in group discussions, and split into smaller group to work on specific topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pyodide.org/"&gt;Pyodide&lt;/a&gt;, &lt;a href="/posts/2022/mamba-meets-jupyterlite/"&gt;Emscripten Forge&lt;/a&gt;, &lt;a href="https://pyscript.net/"&gt;PyScript&lt;/a&gt;, and more generally Python in the browser. This lead to discussions about making WebAssembly a first-class platform for the Python ecosystem, as per &lt;a href="https://discuss.python.org/t/make-wasm-a-1st-class-platform-in-the-python-ecosystem/21798"&gt;https://discuss.python.org/t/make-wasm-a-1st-class-platform-in-the-python-ecosystem/21798&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A proof of concept for using the Language Server Protocol in JupyterLite was also presented, showcasing its potential for improving developer productivity: &lt;a href="https://github.com/jupyterlite/jupyterlite-lsp"&gt;https://github.com/jupyterlite/jupyterlite-lsp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://github.com/jupyterlite/jupyterlite/files/10202654/CAPYTALE_presentation.pdf"&gt;presentation about Capytale and Basthon&lt;/a&gt; and the use of Python in the browser in the French high school system, deployed for 5000+ schools and with more than 200,000 active users per month.&lt;/li&gt;
&lt;li&gt;Integrating JupyterLite and Pyodide in &lt;a href="https://github.com/executablebooks/thebe"&gt;thebe&lt;/a&gt;, for producing interactive documents in arbitrary websites.&lt;/li&gt;
&lt;li&gt;Another topic of work was about using the latest real-time collaboration (RTC) improvements developed upstream in JupyterLab. Enabling RTC on a static JupyterLite website will make it an even more powerful tool for remote teams and online education.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;
&lt;p&gt;You can find the detailed notes and relevant links taken during the workshop:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JupyterLite GitHub discussion: &lt;a href="https://github.com/jupyterlite/jupyterlite/discussions/920"&gt;https://github.com/jupyterlite/jupyterlite/discussions/920&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Jupyter Discourse topic: &lt;a href="https://discourse.jupyter.org/t/jupyterlite-community-workshop-2022-paris-report/17255"&gt;https://discourse.jupyter.org/t/jupyterlite-community-workshop-2022-paris-report/17255&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;Overall, the JupyterLite Community Workshop provided a valuable opportunity for the community to come together and share their knowledge, ideas, and experiences.&lt;/p&gt;
&lt;p&gt;We are really grateful to Bloomberg and Amazon Web Services for their donations to the Jupyter Community Workshops program. This event would not have been possible without their generous support.&lt;/p&gt;
&lt;p&gt;We are also grateful to &lt;a href="https://twitter.com/OVHCloud"&gt;OVHcloud&lt;/a&gt; for hosting this workshop, and for their continued support to the Jupyter project and the NumFOCUS foundation.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/report-on-the-jupyterlite-community-workshop/images/002-1_S2p3XarrcVRgEDcMnJseHg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="community"/><category term="events"/><category term="JupyterLite"/><category term="workshops"/></entry><entry><title>Jupyter Community Workshop: JupyterLite</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/community-workshop-jupyterlite/" rel="alternate"/><published>2022-11-10T15:45:00+00:00</published><updated>2022-11-10T15:45:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2022-11-10:/medium-archive/pelican/posts/2022/community-workshop-jupyterlite/</id><summary type="html">&lt;p&gt;We are thrilled to announce the next in-person Jupyter Community Workshop, which will focus on the JupyterLite project!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are thrilled to announce the next in-person Jupyter Community Workshop, which will focus on the JupyterLite project!&lt;/p&gt;
&lt;p&gt;The event will be held at the &lt;a href="https://www.ovhcloud.com/"&gt;OVHCloud&lt;/a&gt; headquarters in &lt;strong&gt;Paris&lt;/strong&gt;, France, &lt;strong&gt;from December 7th to December 9th&lt;/strong&gt;, 2022. Funding for travel expenses is available for attendees from academia and those from groups which are not well-represented in the Jupyter and wider tech community!&lt;/p&gt;
&lt;p&gt;Jupyter Community Workshop are a series of community-organized events to tackle challenging development and design projects, growing the community of contributors, and strengthening collaborations.&lt;/p&gt;
&lt;p&gt;This specific workshop will focus on the JupyterLite project, a JupyterLab distribution that runs entirely in the browser built from the ground-up using JupyterLab components and extensions. JupyterLite allows for very scalable deployments, and already powers inline consoles and notebooks on the websites of major projects of our ecosystem, such as NumPy, SymPy, Pandas, and many more.&lt;/p&gt;
&lt;p&gt;The workshop will last three days, with hands-on discussions, hacking sessions, and technical presentations. The goal of this event is to foster collaboration and the sharing of knowledge between Jupyter maintainers and downstream library authors and power users.&lt;/p&gt;
&lt;p&gt;Should you be interested in joining us for this workshop, please fill this &lt;a href="https://docs.google.com/forms/d/e/1FAIpQLSc7M_Bmj_u8kdEFrLwnhfj5-T3Y9r37KVb6mlvYVefXh2uSbw/viewform?usp=sf_link"&gt;&lt;strong&gt;form&lt;/strong&gt;&lt;/a&gt;. A limited number of spots are available for this event.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We are grateful to&lt;/em&gt; &lt;a href="https://www.ovhcloud.com/"&gt;&lt;em&gt;OVHCloud&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for hosting this event. We are also grateful to the sponsors of the Jupyter Community Workshop series, Bloomberg and Amazon AWS.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/community-workshop-jupyterlite/images/001-1_Zayr-b0FjuEZGr-plrEl3Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="community"/><category term="events"/><category term="JupyterLite"/><category term="WebAssembly"/><category term="workshops"/></entry><entry><title>Mamba meets JupyterLite</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/" rel="alternate"/><published>2022-07-14T11:19:00+00:00</published><updated>2022-07-15T10:26:00+00:00</updated><author><name>Thorsten Beier</name></author><id>tag:jasongrout.github.io,2022-07-14:/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/</id><summary type="html">&lt;p&gt;Introducing a mamba-based distribution for WebAssembly, and deploying scalable computing environments with JupyterLite.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/images/001-1_dbJO26hiSR8EFygX1rnqrA.webp" alt="JupyterLite logo" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;JupyterLite is a Jupyter distribution that runs entirely in the web browser without any server components. To achieve this, all language kernels must also run in the browser.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A screenshot of JupyterLite running in the Browser. One can see as Matplotlib figure and some Pandas DataFrame code. Furthermore a p5.js kernel instance is visible." src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/images/002-0_MoW-XpW5yQgCxinq.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLite running in the browser as a static website&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A significant benefit of this approach is the &lt;strong&gt;ease of deployment&lt;/strong&gt;. With JupyterLite, the only requirement to provide a live computing environment is a collection of static assets. It makes it possible to embed a console or a notebook interface on any static page or blog without having to deal with a server architecture deployment. The &lt;strong&gt;scalability&lt;/strong&gt; of this approach allowed several major projects of our ecosystem (&lt;a href="https://numpy.org"&gt;NumPy&lt;/a&gt;, &lt;a href="https://www.sympy.org/en/shell.html"&gt;SymPy&lt;/a&gt;, &lt;a href="https://pandas.pydata.org/getting_started.html"&gt;Pandas&lt;/a&gt;, &lt;a href="https://www.pymc.io/welcome.html"&gt;PyMC&lt;/a&gt;, and many more) to embed interactive examples on their websites, which are visited by millions of users monthly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;JupyterLite is the easiest and most scalable way to embed an interactive console or notebook on a web page without any server component.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The most prominent JupyterLite kernel is the &lt;em&gt;Pyolite&lt;/em&gt; Python kernel, which is based on the &lt;a href="https://pyodide.org/en/stable/"&gt;Pyodide&lt;/a&gt; distribution for WebAssembly. Beyond the CPython interpreter, Pyodide includes many popular scientific computing packages such as NumPy, Pandas, and Matplotlib. Pyodide also provides a foreign function interface (FFI) that allows calling Python from JavaScript and vice versa.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLite inline console embedded on the SymPy project website" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/images/003-1_rKzDNlHO6LnhH1ZDyb996g.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLite inline console embedded on the &lt;a href="https://www.sympy.org/en/shell.html"&gt;SymPy project website&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="pyodide-and-beyond"&gt;Pyodide — and beyond&lt;/h2&gt;
&lt;p&gt;While Pyodide provides many scientific computing packages, its monolithic distribution model does not allow to specify package versions, although versions of pure python packages installed on top can be set. Our goal is to enable the &lt;strong&gt;composability of computing environments&lt;/strong&gt; allowed by package managers and to adopt the conda-forge model for large-scale software distribution crowdsourcing.&lt;/p&gt;
&lt;p&gt;Being able to pin down package versions in an environment is a strong requirement for software &lt;strong&gt;reproducibility.&lt;/strong&gt; In fact, a locked WebAssembly environment could be seen as a reproducibility &lt;strong&gt;time capsule&lt;/strong&gt;. As WebAssembly is a recognized web standard, it ought to be runnable for much longer than native binary packages: these are bound to a combination of architecture and platform and will eventually require an emulator.&lt;/p&gt;
&lt;p&gt;This is why we developed a mamba-based distribution of WebAssembly packages built with Emscripten.&lt;/p&gt;
&lt;h2 id="emscripten-forge"&gt;&lt;strong&gt;Emscripten-forge&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The choice of the Mamba/Conda package manager was natural. Its main strength is the conda-forge community-maintained distribution, which has become the &lt;em&gt;de facto&lt;/em&gt; standard source of packages for scientific computing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beyond its solid technological foundations and the multi-platform nature of the conda-forge distribution, its main strength is its social model. It allowed for a crowdsourcing approach of the packaging problem, with a balance of separation of concerns between maintainer teams and across-the-board automation, plus an amazing maintainers community.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We plan on contributing this work to the conda-forge project, so that all recipes live in the same space.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Mamba/Conda package manager has support for many platforms and architectures such as Linux, OS X (for both x86 and arm64), and Windows. However, the &lt;strong&gt;WebAssembly&lt;/strong&gt; family of platforms is not supported yet.&lt;/p&gt;
&lt;h3 id="adding-support-for-webassembly-to-mamba-conda"&gt;Adding support for WebAssembly to mamba &amp;amp; conda&lt;/h3&gt;
&lt;p&gt;To create conda packages for the WebAssembly platform, we relied on the &lt;a href="https://emscripten.org/"&gt;Emscripten toolchain&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We defined a new target platform for conda-build and boa, namely &lt;code&gt;wasm32-unknown-emscripten&lt;/code&gt; for which we use the &lt;code&gt;emscripten-32&lt;/code&gt; shorthand name. We then associated the &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt; compiler, &lt;a href="https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes/emscripten_emscripten-32"&gt;wrapped in a conda package&lt;/a&gt; as the C/C++ compiler for this new target.&lt;br&gt;
This already allowed us to build many packages, including simple libraries like &lt;code&gt;bzip2&lt;/code&gt; and &lt;code&gt;zlib&lt;/code&gt;, but also more complex packages like &lt;code&gt;Python&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For Python extension modules we used &lt;a href="http://crossenv"&gt;&lt;code&gt;crossenv&lt;/code&gt;&lt;/a&gt; which can create virtual environments for cross-compiling, and &lt;a href="https://github.com/conda-forge/cross-python-feedstock"&gt;&lt;code&gt;cross-python&lt;/code&gt;&lt;/a&gt; which integrates &lt;a href="http://crossenv"&gt;&lt;code&gt;crossenv&lt;/code&gt;&lt;/a&gt; into conda. All the code and recipes for cross-compilation are hosted on the &lt;a href="https://github.com/emscripten-forge/recipes"&gt;emscripten-forge GitHub repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We then used &lt;a href="https://github.com/emscripten-forge/recipes/blob/main/.github/workflows/build_recipes.yaml"&gt;GitHub actions&lt;/a&gt; to build packages with Emscripten and upload them to a package server.&lt;/li&gt;
&lt;li&gt;Packages are hosted on a deployment of the &lt;a href="https://github.com/mamba-org/quetz"&gt;Quetz&lt;/a&gt; open-source server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this, you can easily create an environment for the &lt;code&gt;emscripten-32&lt;/code&gt; target:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;micromamba create -n my-env --platform=emscripten-32 \ 
    -c https://repo.mamba.pm/emscripten-forge \ 
    -c https://repo.mamba.pm/conda-forge \
    python ipython numpy jedi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that we not only added emscripten-forge as a channel, but also &lt;a href="https://repo.mamba.pm/conda-forge"&gt;conda-forge&lt;/a&gt;. This means all noarch packages can be used.&lt;/p&gt;
&lt;h3 id="adding-new-packages-to-the-emscripten-forge-channel"&gt;Adding new packages to the emscripten-forge channel&lt;/h3&gt;
&lt;p&gt;Adding new packages is a simple procedure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fork the repository &lt;a href="https://github.com/emscripten-forge/recipes"&gt;https://github.com/emscripten-forge/recipes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;create a folder for your package in &lt;a href="https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten"&gt;&lt;strong&gt;recipes/recipes_emscripten/&amp;lt;my_package&amp;gt;&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;add a &lt;a href="https://github.com/emscripten-forge/recipes/blob/main/recipes/recipes_emscripten/widgetsnbextension/recipe.yaml"&gt;&lt;code&gt;recipe.yaml&lt;/code&gt;&lt;/a&gt; for your package in &lt;code&gt;recipes/recipes_emscripten/&amp;lt;your_package&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;create a pull request containing the recipe. Once the pull request is merged, the package is automatically uploaded to the &lt;code&gt;emscripten-forge&lt;/code&gt; channel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="integration-with-jupyterlite"&gt;Integration with JupyterLite&lt;/h2&gt;
&lt;p&gt;Even though this is a general-purpose conda-based distribution for Emscripten packages, we had one particular application in mind for this first iteration: &lt;strong&gt;JupyterLite&lt;/strong&gt;. The existing Pyolite kernel is too tightly coupled with the Pyodide distribution, so we decided to go with &lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;&lt;strong&gt;xeus-python&lt;/strong&gt;&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;The main reason for picking &lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;xeus-python&lt;/a&gt; (over ipykernel) is that with xeus-based kernels, it is possible to override the communication layer of the kernel (switching e.g. from ZMQ to HTTP/2). In the case of JupyterLite, the implementation simply relies on direct JavaScript function calls.&lt;/p&gt;
&lt;p&gt;You can check out our &lt;a href="/posts/2021/xeus-lite/"&gt;earlier blog post&lt;/a&gt; for more details on the JupyterLite xeus-based kernels.&lt;/p&gt;
&lt;h3 id="providing-a-complete-python-development-experience"&gt;Providing a complete Python development experience&lt;/h3&gt;
&lt;p&gt;Some remaining intrinsic limitations to the WebAssembly platform need to be worked around to provide a complete experience to end-users. For example, sockets cannot be created in WebAssembly, preventing the use of the default asyncio event loop implementation. Luckily, the Pyodide authors developed a custom asyncio event-loop called &lt;a href="https://pyodide.org/en/latest/usage/api/python-api/webloop.html"&gt;WebLoop&lt;/a&gt;: it wraps the browser event loop using the Python — JavaScript foreign function interface (FFI) provided with &lt;a href="https://pyodide.org/en/stable/usage/type-conversions.html"&gt;Pyodide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pyjs:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since it is non-trivial to extract Pyodide’s FFI and use it for other projects, we created a modern Python - JavaScript FFI from scratch. This was done with the following tricks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pybind/pybind11"&gt;Pybind11&lt;/a&gt; is used to call Python from C++ and vice versa,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html"&gt;Embind&lt;/a&gt; is used to call JavaScript from C++ and vice versa.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we use &lt;a href="https://github.com/pybind/pybind11"&gt;Pybind11&lt;/a&gt; and &lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html"&gt;Embind&lt;/a&gt; together we can call Python from JavaScript and vice versa, with C++ as a man in the middle. This not only allows us to write a simple FFI from scratch with relatively little code but also avoids calling any low-level CPython APIs and enables using high-level constructs — like &lt;a href="https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html#calling-python-functions"&gt;&lt;code&gt;pybind11::object&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://emscripten.org/docs/api_reference/val.h.html"&gt;&lt;code&gt;emscripten::val&lt;/code&gt;&lt;/a&gt;— instead.&lt;br&gt;
The code is available in the &lt;a href="https://github.com/emscripten-forge/pyjs"&gt;pyjs&lt;/a&gt; repository. The API is very similar to Pyodide’s so that it can be used as a drop-in replacement in code, like Pyodide’s &lt;a href="https://pyodide.org/en/latest/usage/api/python-api/webloop.html"&gt;WebLoop&lt;/a&gt; implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deployment:&lt;/strong&gt;The &lt;a href="https://github.com/jupyterlite/xeus-python-kernel"&gt;xeus-python-kernel&lt;/a&gt; allows conda packages to be pre-installed in the Python runtime. This can be done by passing the &lt;code&gt;XeusPythonEnv.packages&lt;/code&gt; CLI option to &lt;code&gt;jupyter lite build&lt;/code&gt;. The following command will install &lt;code&gt;NumPy&lt;/code&gt;, &lt;code&gt;Matplotlib&lt;/code&gt;, and &lt;code&gt;ipyleaflet&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;jupyter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lite&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;XeusPythonEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;numpy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;matplotlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;\
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;ipyleaflet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="xeus-python kernel with the ipyleaflet widget visible." src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/mamba-meets-jupyterlite/images/004-1_JCiZIwwkFen5kwEA2rK4SA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Running the xeus-python kernel with the ipyleaflet widget in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;More details can be found in the &lt;a href="https://github.com/jupyterlite/xeus-python-kernel"&gt;xeus-python-kernel GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-about-the-future"&gt;What about the future?&lt;/h2&gt;
&lt;p&gt;This combination of JupyterLite and Mamba has the potential to open Jupyter to millions of additional users.&lt;br&gt;
Given its scalability, ease of deployment, reproducibility, and accessibility, JupyterLite will be everywhere: countries, organizations, and schools that don’t have access to sovereign cloud infrastructure will be able to deploy Jupyter-based education platforms on servers that they truly own, without endangering the data of their students or becoming too reliant on resources that they do not control.&lt;/p&gt;
&lt;h3 id="in-the-short-term-we-are-working-on-the-following-next-steps"&gt;In the short term, we are working on the following “next steps”:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mambalite:&lt;/strong&gt; To support the installation of packages at runtime. Similar to Pyodide’s pip-lite, it will allow downloading packages at runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fortran:&lt;/strong&gt; Compiling Fortran code with Emscripten is currently not supported, but it is necessary for key packages like SciPy. Pyodide relies on f2c, a Fortran-to-C converter, in conjunction with a set of patches to compile Fortran code with Emscripten. We are working on a more direct approach: compiling SciPy natively with &lt;a href="https://lfortran.org/"&gt;LFortran&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binderlite:&lt;/strong&gt; Binder converts a repository of notebooks into an executable JupyterLab environment, making code immediately reproducible by anyone, anywhere. Emscripten-forge is the missing piece to build BinderLite, a version of Binder relying on JupyterLite instances instead of vanilla JupyterLab instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rust/PyO3 support:&lt;/strong&gt; We are working on integrating the &lt;a href="https://blog.pyodide.org/posts/rust-pyo3-support-in-pyodide/"&gt;work of the Pyodide team&lt;/a&gt; on Rust/PyO3 support in emscripten-forge. This will be important to build Rust extension modules like &lt;code&gt;cryptography&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="credits"&gt;Credits&lt;/h2&gt;
&lt;p&gt;This was built upon the work of a much bigger crowd!&lt;/p&gt;
&lt;h3 id="the-pyodide-team"&gt;The Pyodide team&lt;/h3&gt;
&lt;p&gt;The Pyodide project was started at the Mozilla foundation by &lt;a href="https://twitter.com/MDroettboom"&gt;Michael Droettboom&lt;/a&gt; and is now maintained by &lt;a href="https://github.com/hoodmane"&gt;Hood Chatham&lt;/a&gt;, &lt;a href="https://twitter.com/RomanYurchak"&gt;Roman Yurchak&lt;/a&gt;, and &lt;a href="https://github.com/ryanking13"&gt;Gyeongjae Choi&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The foundational work of the Pyodide project pioneered the use of Python in the browser and made all of the rest possible, from JupyterLite to this work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="the-emscripten-team"&gt;The Emscripten team&lt;/h3&gt;
&lt;p&gt;Both Pyodide and emscripten-forge are built upon the Emscripten toolchain, which provides the foundational components to be able to meaningfully run WebAssembly programs in the browser.&lt;/p&gt;
&lt;h3 id="the-jupyterlite-team"&gt;&lt;strong&gt;The JupyterLite team&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The JupyterLite project was started by &lt;a href="https://twitter.com/jtpio"&gt;Jeremy Tuloup&lt;/a&gt;, with significant contributions from &lt;a href="https://github.com/bollwyvl"&gt;Nick Bollweg&lt;/a&gt; and &lt;a href="https://twitter.com/martinrenou"&gt;Martin Renou&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="the-mamba-org-team"&gt;The Mamba Org team&lt;/h3&gt;
&lt;p&gt;The mamba ecosystem has been instrumental in making these developments possible. We use the Quetz open-source server for hosting the packages and the Boa tool to build them. In the mamba development team, we should highlight the work of &lt;a href="https://twitter.com/wuoulf"&gt;Wolf Vollprecht&lt;/a&gt;, &lt;a href="https://twitter.com/johanmabille"&gt;Johan Mabille&lt;/a&gt;, &lt;a href="https://twitter.com/MJKlaim"&gt;Joel Lamotte&lt;/a&gt;, and &lt;a href="https://twitter.com/atrawog"&gt;Andreas Trawöger&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="the-xeus-team"&gt;The Xeus team&lt;/h3&gt;
&lt;p&gt;The xeus project was started by &lt;a href="https://twitter.com/johanmabille"&gt;Johan Mabille&lt;/a&gt; and &lt;a href="https://twitter.com/JohanMabille/"&gt;Sylvain Corlay&lt;/a&gt;. It is at the foundation of the JupyterLite integration and helped to get all the pieces together (Xeus, Mamba, Jupyter). We should especially credit the work of &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; and &lt;a href="https://twitter.com/thorstenbeier"&gt;Thorsten Beier&lt;/a&gt; on this integration with JupyterLite.&lt;/p&gt;
&lt;h2 id="acknowledgment"&gt;Acknowledgment&lt;/h2&gt;
&lt;p&gt;The work of Thorsten Beier, Johan Mabille, Martin Renou, Sylvain Corlay, Wolf Vollprecht, Joel Lamotte, and Andreas Trawoger at &lt;a href="https://twitter.com/QuantStack"&gt;&lt;strong&gt;QuantStack&lt;/strong&gt;&lt;/a&gt; was funded by &lt;a href="https://twitter.com/TechAtBloomberg?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the Authors&lt;/h2&gt;
&lt;h3 id="thorsten-beier"&gt;&lt;strong&gt;Thorsten Beier&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://twitter.com/thorstenbeier"&gt;Thorsten Beier&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;. Before joining &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt;, he graduated in computer science at the University of Heidelberg and worked at the &lt;a href="https://www.embl.org/"&gt;EMBL&lt;/a&gt;. As an open-source developer, Thorsten worked on a variety of projects, from &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; and &lt;a href="https://github.com/QuantStack/xtensor"&gt;xtensor&lt;/a&gt; in C++ to &lt;a href="https://github.com/inferno-pytorch/inferno"&gt;inferno&lt;/a&gt;, &lt;a href="https://kipoi.org/"&gt;kipoi&lt;/a&gt;, &lt;a href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt;, and &lt;a href="https://github.com/uhlmanngroup/napari-splineit"&gt;napari-splineit&lt;/a&gt; in Python.&lt;/p&gt;
&lt;h3 id="martin-renou"&gt;Martin Renou&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;. Before joining &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;, he studied at the French Aerospace Engineering School &lt;a href="https://www.isae-supaero.fr/en"&gt;SUPAERO&lt;/a&gt;. He also worked at Logilab in Paris and Enthought in Cambridge. As an open-source developer at &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;, Martin worked on a variety of projects, from &lt;a href="https://github.com/QuantStack/xsimd"&gt;xsimd&lt;/a&gt;, &lt;a href="https://github.com/QuantStack/xtensor"&gt;xtensor&lt;/a&gt;, and &lt;a href="https://github.com/QuantStack/xframe"&gt;xframe&lt;/a&gt; in C++ to &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; and &lt;a href="https://github.com/maartenbreddels/ipywebrtc"&gt;ipywebrtc&lt;/a&gt; in Python and JavaScript.&lt;/p&gt;
</content><category term="JupyterLite"/><category term="WebAssembly"/></entry><entry><title>Jupyter Everywhere</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/" rel="alternate"/><published>2022-03-15T15:22:00+00:00</published><updated>2022-03-15T15:38:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2022-03-15:/medium-archive/pelican/posts/2022/jupyter-everywhere/</id><summary type="html">&lt;p&gt;Easily embed a console, a notebook, or a fully-fledged IDE on any web page.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Easily embed a console, a notebook, or a fully-fledged IDE on any web page.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In a &lt;a href="/posts/2021/jupyterlite-jupyter-webassembly-python/"&gt;previous blog post&lt;/a&gt;, we announced JupyterLite, a JupyterLab distribution that runs entirely in the web browser, backed by in-browser language kernels.&lt;/p&gt;
&lt;p&gt;By default, JupyterLite ships with a Python kernel powered by &lt;a href="https://pyodide.org"&gt;Pyodide&lt;/a&gt; and &lt;a href="https://ipython.readthedocs.io/en/stable"&gt;IPython&lt;/a&gt;, bringing a wide variety of features from &lt;strong&gt;code completion&lt;/strong&gt; to &lt;strong&gt;interactive visualizations&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The latest releases of JupyterLite now ship with a REPL application by default, that you can embed in any website.&lt;/p&gt;
&lt;h2 id="embed-a-live-python-console-on-your-website"&gt;Embed a live Python console on your website 🚀&lt;/h2&gt;
&lt;p&gt;Do you want to add an interactive code console to your website, so your visitors can run Python code directly in their browser without installing anything?&lt;/p&gt;
&lt;p&gt;Let’s say you already have a static website up and running. This could for example be a Jekyll blog. With the JupyterLite REPL, you can embed the interactive console in your website with something as simple as the following code snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://jupyterlite.github.io/demo/repl/index.html?kernel=python&amp;amp;toolbar=1&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;500px&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there you have it, a live Python REPL embedded in your blog! 🎉&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="An example of embedding a live Python console in a Jekyll blog." src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/001-1_6o4AQYSXu5D0FUEDuKN12g.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;An example of embedding a live Python console in a Jekyll blog.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This example relies on the publicly available &lt;a href="https://github.com/jupyterlite/demo"&gt;demo website&lt;/a&gt; deployed to GitHub Pages. However we recommend setting up your own JupyterLite deployment, so you can have full control and use custom configuration and extensions (see the &lt;em&gt;Deploying your JupyterLite website&lt;/em&gt; section below).&lt;/p&gt;
&lt;h2 id="powering-the-numpy-documentation"&gt;Powering the NumPy documentation 🐍&lt;/h2&gt;
&lt;p&gt;The NumPy project had already been using an interactive console on the &lt;a href="https://numpy.org"&gt;numpy.org&lt;/a&gt; documentation website. The previous console was powered by &lt;a href="https://github.com/executablebooks/thebe"&gt;Thebe&lt;/a&gt; and &lt;a href="https://mybinder.org"&gt;mybinder.org&lt;/a&gt;. While this gave great flexibility and a way for users to try NumPy without installing anything on their machine, it required starting a new Jupyter Server on Binder for each user, and then waiting for a kernel to start before being able to type some code.&lt;/p&gt;
&lt;p&gt;This setup worked for a while but started to become more difficult to maintain and operate.&lt;/p&gt;
&lt;p&gt;With the JupyterLite-powered Python REPL, &lt;a href="https://numpy.org"&gt;numpy.org&lt;/a&gt; is now able to provide an interactive code console and let anyone try NumPy in their browsers. This considerably reduces the number of resources needed and allows for a smoother and snappier user experience.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLite REPL on numpy.org" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/002-1_aBGC8_llGDnbWifezqJBHQ.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLite REPL on numpy.org&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The REPL is currently based on the JupyterLab Code Console, which gives a familiar “look and feel” to Jupyter users. Other user interfaces such as a &lt;a href="https://github.com/jupyterlite/jupyterlite/issues/148"&gt;Single Executable Cell&lt;/a&gt; are also being considered for the next version of the REPL.&lt;/p&gt;
&lt;h2 id="try-jupyter"&gt;Try Jupyter 🟠&lt;/h2&gt;
&lt;p&gt;Another recent adopter of the JupyterLite stack is the official Jupyter website.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://try.jupyter.org"&gt;try.jupyter.org&lt;/a&gt; has been using the public-facing &lt;a href="https://mybinder.org"&gt;mybinder.org&lt;/a&gt; deployment to let users try the different Jupyter subprojects, such as &lt;a href="/posts/2019/and-voila/"&gt;Voilà&lt;/a&gt; and the &lt;a href="/posts/2020/xeus-is-now-a-jupyter-subproject/"&gt;Xeus-based&lt;/a&gt; kernels.&lt;/p&gt;
&lt;p&gt;This setup has been working nicely for many years. However, the Binder project recently faced &lt;a href="https://github.com/jupyterhub/mybinder.org-deploy/issues/2138"&gt;funding issues&lt;/a&gt;, which drastically reduced the overall capacity of the Binder federation. This means fewer resources are available to users for their projects, and this also impacted the Try Jupyter website.&lt;/p&gt;
&lt;p&gt;To mitigate this, we decided to create a custom JupyterLite website and use it for some of the demos on Try Jupyter. Users can then continue to try some of the Jupyter interfaces in their browsers without installing anything on their machines. As a result, the JupyterLab and Jupyter Notebook demos now point to the new JupyterLite deployment.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Launching JupyterLite from try.jupyter.org" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/003-1_UZ42uWfkmSasuyGWFRI2ZQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Launching JupyterLite from try.jupyter.org&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The “Try Jupyter with JupyterLite” deployment also includes the &lt;a href="https://github.com/jupyterlab-contrib/jupyterlab-tour"&gt;jupyterlab-tour&lt;/a&gt; extension, offering a user-friendly tour of the Jupyter interface to newcomers.&lt;/p&gt;
&lt;h2 id="deploying-your-jupyterlite-website"&gt;Deploying your JupyterLite website 💡&lt;/h2&gt;
&lt;p&gt;Contrary to a regular Jupyter deployment, JupyterLite can be served as a simple static website without running a Python server. This makes it &lt;strong&gt;simpler to deploy, and cheaper to operate&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Below are some examples for deploying your own JupyterLite and Python REPL.&lt;/p&gt;
&lt;h3 id="deploy-to-github-pages"&gt;Deploy to GitHub Pages&lt;/h3&gt;
&lt;p&gt;GitHub Pages is one of the simplest ways to host a JupyterLite website.&lt;/p&gt;
&lt;p&gt;This is what the demo repository available at &lt;a href="https://github.com/jupyterlite/demo"&gt;https://github.com/jupyterlite/demo&lt;/a&gt; does by default. It lets you bootstrap your JupyterLite website with a few clicks, and deploy it to GitHub Pages within a couple of minutes.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Creating a JupyterLite website with just a couple of clicks" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/004-1_fK9jSMoHgqBpnKN5aLXlIg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Creating a JupyterLite website with just a couple of clicks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After a few minutes, your website will be ready and you can then embed your live Python console with the following snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://yourhandle.github.io/my-custom-deployment/repl/index.html?kernel=python&amp;amp;toolbar=1&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;500px&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLite REPL running on GitHub Pages" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/005-1_KkyYclq5t2YyyuUhFQfbEw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLite REPL running on GitHub Pages&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="deploy-to-vercel-or-netlify"&gt;Deploy to Vercel or Netlify&lt;/h3&gt;
&lt;p&gt;Hosted platforms like Vercel and Netlify are good options to deploy a static JupyterLite website.&lt;/p&gt;
&lt;p&gt;The REPLite project is an example deployed to Vercel, based on the following repository with some custom configuration: &lt;a href="https://github.com/jtpio/replite"&gt;https://github.com/jtpio/replite&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Python REPL can be embedded with an IFrame:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://replite.vercel.app/repl?kernel=python&amp;amp;toolbar=1&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we use the &lt;code&gt;?kernel=python&lt;/code&gt; URL parameter to automatically choose the Python kernel on startup.&lt;/p&gt;
&lt;h3 id="deploying-to-readthedocs-using-the-sphinx-extension"&gt;Deploying to ReadTheDocs using the Sphinx extension&lt;/h3&gt;
&lt;p&gt;Going the extra mile, &lt;a href="https://github.com/jupyterlite/jupyterlite-sphinx"&gt;jupyterlite-sphinx&lt;/a&gt; brings the power of JupyterLite to your Sphinx documentation. It automatically creates a JupyterLite deployment in your documentation website and provides some utilities for using that deployment easily.&lt;/p&gt;
&lt;p&gt;If you are developing software, it’s a very convenient tool to let your users test your library directly on the documentation page!&lt;/p&gt;
&lt;p&gt;This is also really useful for Jupyter users who want to easily share their work on &lt;a href="http://readthedocs.org"&gt;readthedocs.org&lt;/a&gt;, GitHub Pages, or any other host that supports Sphinx documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can install &lt;code&gt;jupyterlite-sphinx&lt;/code&gt; with pip:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install jupyterlite-sphinx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add the extension to your &lt;code&gt;conf.py&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;extensions = [
    &amp;#39;jupyterlite_sphinx&amp;#39;,
    # And other sphinx extensions
    # ...
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that’s it! &lt;code&gt;jupyterlite-sphinx&lt;/code&gt; will automatically deploy a JupyterLite website when building your docs with Sphinx.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jupyterlite-sphinx&lt;/code&gt; provides multiple Sphinx directives for embedding Python consoles and custom Notebooks in your documentation:&lt;/p&gt;
&lt;p&gt;To embed a Python REPL similar to the &lt;a href="http://NumPy.org"&gt;NumPy.org&lt;/a&gt; one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="ow"&gt;replite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;
   &lt;span class="nc"&gt;:kernel:&lt;/span&gt; python
   &lt;span class="nc"&gt;:theme:&lt;/span&gt; JupyterLab Light
   &lt;span class="nc"&gt;:width:&lt;/span&gt; 100%
   &lt;span class="nc"&gt;:height:&lt;/span&gt; 600px
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    print(&amp;#39;Hello from a JupyterLite console!&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Embedding the JupyterLite REPL" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/006-0_DMYBurEcdHmylCDu.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Embedding the JupyterLite REPL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To embed a full Notebook:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="ow"&gt;retrolite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt; my_notebook.ipynb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;a href="https://ipycanvas.readthedocs.io/en/master"&gt;&lt;img alt="An interactive Jupyter Notebook on the ipycanvas documentation" src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/007-1_CCLLn1s1p9GEFwHB6WAH-w.jpg" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;An interactive Jupyter Notebook on the &lt;a href="https://ipycanvas.readthedocs.io/en/master"&gt;ipycanvas documentation&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, you might also want to embed the full JupyterLite UI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;..&lt;/span&gt; &lt;span class="ow"&gt;jupyterlite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that the notebooks are saved in the browser local storage, so any work done on those Notebooks can be retrieved later when coming back to the web page with the same browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can find the &lt;code&gt;jupyterlite-sphinx&lt;/code&gt; documentation with live examples following this link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterlite-sphinx.readthedocs.io"&gt;jupyterlite-sphinx — JupyterLite sphinx extension documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can find an example of &lt;code&gt;jupyterlite-sphinx&lt;/code&gt; being used on &lt;a href="https://readthedocs.org/"&gt;ReadTheDocs&lt;/a&gt; here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ipycanvas.readthedocs.io/en/master"&gt;ipycanvas: Interactive Canvas in Jupyter — ipycanvas documentation&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="more-examples"&gt;More examples&lt;/h3&gt;
&lt;p&gt;JupyterLite can be deployed on more platforms than just the ones mentioned above. Pretty much anything that can serve static files should work!&lt;/p&gt;
&lt;p&gt;Feel free to check out the documentation to learn more about deploying your JupyterLite website on other platforms: &lt;a href="https://jupyterlite.readthedocs.io/en/latest/deploying.html#hosted"&gt;https://jupyterlite.readthedocs.io/en/latest/deploying.html#hosted&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="jupyter-everywhere"&gt;Jupyter Everywhere 🌐&lt;/h2&gt;
&lt;p&gt;With JupyterLite gaining rapid adoption on widely visited websites such as &lt;a href="https://numpy.org"&gt;numpy.org&lt;/a&gt; and &lt;a href="https://try.jupyter.org"&gt;try.jupyter.org&lt;/a&gt;, we envision Jupyter being adopted on more websites and documentation over the next months.&lt;/p&gt;
&lt;p&gt;We hope to bring interactive computing with Jupyter to even more people than before and make it more accessible by lowering the barrier of entry.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/008-0_TK59W6sOJHNg-Sgi.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Because JupyterLite can easily be extended with JupyterLab extensions, we also expect more Jupyter-based deployments and applications to flourish, making good reuse of the existing components that constitute the vibrant Jupyter ecosystem 🌸&lt;/p&gt;
&lt;p&gt;Jupyter is language agnostic and more kernels are now available in JupyterLite, for example for Lua and SQLite based on the &lt;a href="/posts/2021/xeus-lite/"&gt;Xeus framework&lt;/a&gt;. Soon Jupyter will also power the documentation and websites of other languages, not just Python.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the authors&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/009-0_bIbm1NNbif5Dn1H8.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://quantstack.net/"&gt;QuantStack&lt;/a&gt; and a Jupyter Distinguished Contributor. Martin works on a variety of projects, from &lt;a href="https://github.com/QuantStack/xtensor"&gt;xtensor&lt;/a&gt; and &lt;a href="https://github.com/QuantStack/xeus-python/"&gt;xeus-python&lt;/a&gt; in C++ to &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt;, &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; and &lt;a href="https://github.com/bqplot/bqplot"&gt;bqplot&lt;/a&gt; in Python and TypeScript.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/010-1_rBRr_6uBceP-shArkLcXhQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/jtpio"&gt;Jeremy Tuloup&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; and a Jupyter Distinguished Contributor. Maintainer and contributor of JupyterLab, Jupyter Notebook, JupyterLite, Voilà, and projects within the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2022/jupyter-everywhere/images/011-1_Ab9PIO4Zs-6MOYhzF4GXPA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLite"/></entry><entry><title>JupyterLite: Jupyter ❤️ WebAssembly ❤️ Python</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/" rel="alternate"/><published>2021-07-13T09:11:00+00:00</published><updated>2021-07-13T09:11:00+00:00</updated><author><name>Jeremy Tuloup</name></author><id>tag:jasongrout.github.io,2021-07-13:/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/</id><summary type="html">&lt;p&gt;JupyterLite is a JupyterLab distribution that runs entirely in the web browser, backed by in-browser language kernels powered by…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;JupyterLite is a JupyterLab distribution that runs entirely in the web browser, backed by in-browser language kernels.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/001-1_CMvcTaLSAD5A-WHCtnIFwA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;
&lt;p&gt;JupyterLite is a reboot of several attempts at making a full static Jupyter distribution that runs in the browser, without having to start the Python Jupyter Server on the host machine, usually done by running &lt;code&gt;jupyter lab&lt;/code&gt; or &lt;code&gt;jupyter notebook&lt;/code&gt; in a terminal.&lt;/p&gt;
&lt;p&gt;The goal of the project is to provide a lightweight computing environment accessible in a matter of seconds with a single click, in a web browser, and without having to install anything on the end-user device.&lt;/p&gt;
&lt;p&gt;With in-browser distributions, there is no need to provision the execution environment in the backend. Since the application is mostly a set of static files, it scales more easily, and it is also easier to deploy.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterLite running in the browser as a static website on ReadTheDocs" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/002-1_bR--bXsuiMIDz-D_V4BfLw.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLite running in the browser as a static website on ReadTheDocs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="a-full-jupyterlab-distribution-running-in-the-browser"&gt;A full JupyterLab distribution running in the browser&lt;/h2&gt;
&lt;p&gt;JupyterLite is built from the ground up, reusing many JupyterLab plugins and components as is.&lt;/p&gt;
&lt;p&gt;In addition to JupyterLab, JupyterLite also includes the &lt;a href="https://github.com/jupyterlab/retrolab"&gt;RetroLab&lt;/a&gt; interface by default:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterLite with the RetroLab interface" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/003-1_B1Se7Fe1JXBt2a1NjwRmxg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLite with the RetroLab interface&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By reusing JupyterLab components, JupyterLite benefits from many of the upstream improvements such as new features, accessibility fixes, and upkeep improvements. The &lt;a href="/posts/2021/how-we-made-jupyter-notebooks-collaborative-with-yjs/"&gt;recent work on real time collaboration&lt;/a&gt; coming in JupyterLab 3.1 and championed by &lt;a href="https://twitter.com/kevin_jahns"&gt;Kevin Jahns&lt;/a&gt;, &lt;a href="https://twitter.com/carlosHerreroB/"&gt;Carlos Herrero&lt;/a&gt;, and &lt;a href="https://twitter.com/echarles"&gt;Eric Charles&lt;/a&gt; can be enabled in JupyterLite too!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Real Time Collaboration with JupyterLite on ReadTheDocs" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/004-1_HwMx3Fd6iICkWjjvUCtvwA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Real Time Collaboration with JupyterLite on ReadTheDocs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="pyolite-a-python-kernel-backed-by-pyodide"&gt;Pyolite, a Python kernel backed by Pyodide&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://pyodide.org"&gt;Pyodide&lt;/a&gt; consists of the CPython 3.8 interpreter compiled to WebAssembly which allows Python to run in the browser. Many popular scientific Python packages have also been compiled and made available. In addition, Pyodide can install any Python package with a pure Python wheel from the Python Package Index (PyPI). Pyodide also includes a comprehensive foreign function interface that exposes the ecosystem of Python packages to JavaScript and the browser user interface, including the DOM, to Python.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Pyodide: Python with the scientific stack, compiled to WebAssembly" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/005-1_usFUmvzRo6v8TuRFQhK0uw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Pyodide: Python with the scientific stack, compiled to WebAssembly&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Currently at version &lt;a href="https://hacks.mozilla.org/2021/04/pyodide-spin-out-and-0-17-release/"&gt;0.17&lt;/a&gt;, Pyodide has benefited from many improvements over the past years: smaller binary size, support for &lt;code&gt;asyncio&lt;/code&gt;, and better type translations between Python and JavaScript.&lt;/p&gt;
&lt;p&gt;JupyterLite ships by default with &lt;code&gt;Pyolite&lt;/code&gt;, a Python kernel backed by Pyodide. Pyolite runs in a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API"&gt;Web Worker&lt;/a&gt; and thus doesn’t block the main UI thread when intensive computations are executed.&lt;/p&gt;
&lt;h2 id="ipython-in-the-browser"&gt;IPython in the browser&lt;/h2&gt;
&lt;p&gt;Thanks to the work by Madhur Tandon in this &lt;a href="https://github.com/jupyterlite/jupyterlite/pull/171"&gt;pull request&lt;/a&gt;, Pyolite is now powered by IPython. This provides access to magics, code completion, rich display, interactive widgets, and many other features.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Using IPython in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/006-1_2WBnuwRGkASGVNYUGjyPHg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using IPython in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="interactive-visualization"&gt;Interactive Visualization&lt;/h2&gt;
&lt;p&gt;Many visualizations libraries such as &lt;a href="https://altair-viz.github.io/"&gt;Altair&lt;/a&gt; and &lt;a href="https://plotly.com/"&gt;Plotly&lt;/a&gt; are also supported in JupyterLite, allowing for quick and convenient figures and plots right in the browser:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Using Altair in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/007-1_SRr162bkckWsmoSMxVd2uQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using Altair in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Creating Plotly figures in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/008-1_w7Y4wRz9h2OFMgm5MfdD0A.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Creating Plotly figures in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="support-for-jupyter-widgets"&gt;Support for Jupyter Widgets&lt;/h2&gt;
&lt;p&gt;Jupyter Widgets rely on the &lt;a href="https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages"&gt;Custom Messages specification of the Jupyter Protocol&lt;/a&gt; to send messages back and forth between the kernel and the frontend. This &lt;a href="https://github.com/jupyterlite/jupyterlite/pull/145"&gt;pull request&lt;/a&gt; by Martin Renou added support for Comms in the Pyolite kernel, which enabled many of the existing core and third-party Jupyter Widgets to work out of the box in JupyterLite such as &lt;a href="https://github.com/bqplot/bqplot"&gt;bqplot&lt;/a&gt;, &lt;a href="https://github.com/jupyter-widgets/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; and &lt;a href="https://github.com/martinRenou/ipycanvas"&gt;ipycanvas&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterLite comes with support for Jupyter Widgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/009-1_c1DIbxS6bZ7bHDcOTEIJfg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLite comes with support for Jupyter Widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="more-than-just-python"&gt;More than just Python&lt;/h2&gt;
&lt;p&gt;JupyterLite makes it possible to have plenty of other kernels also running in the browser. For now, the default distribution includes a JavaScript and a &lt;a href="https://p5js.org/"&gt;p5&lt;/a&gt; kernel:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Multiple kernels are available in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/010-1_5XFdmBdQWeONbRD3nuQe0A.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Multiple kernels are available in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Both the JavaScript and the p5 kernels run in an IFrame as the code execution sandbox. With the Jupyter display protocol, users can easily render custom animations in the browser:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The p5.js kernel in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/011-1_kPYtSDP3aE5_M5AmDT-Uvw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The p5.js kernel in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="highly-customizable"&gt;Highly Customizable&lt;/h2&gt;
&lt;p&gt;Just like many existing Jupyter tools, JupyterLite can easily be configured for custom needs.&lt;/p&gt;
&lt;p&gt;It supports the new &lt;a href="https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#prebuilt-extensions"&gt;JupyterLab prebuilt extension system&lt;/a&gt; added to the 3.0 release. Existing JupyterLab extensions can then easily be reused in JupyterLite too!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLab Drawio extension running in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/012-1_69CnccBrufVEEU0wqE_f7Q.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLab Drawio extension running in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The in-browser server part of JupyterLite also follows a plugin-based approach. The server is a &lt;a href="https://github.com/jupyterlab/lumino"&gt;Lumino&lt;/a&gt; application without a shell and registers multiple plugins such as kernels, the contents manager or the sessions service. This plugin-based approach makes it very convenient for deployers and site administrators to swap a plugin for another one. For instance, they might want to swap the default contents manager that stores notebooks and files in the browser local storage with another plugin that would save them on AWS S3 instead.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Overview of the plugin-based architecture of JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/013-1_f7viaaS4SYTBpjA6DTPsDQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Overview of the plugin-based architecture of JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The modularity and flexibility of JupyterLite make it possible to easily add new kernels. For example, the Basthon kernel uses a slightly different execution model than Pyolite. It runs in the main UI thread so users can manipulate the main window DOM from within Python directly, while Pyolite runs in a Web Worker as a background thread. Both approaches have pros and cons, and the JupyterLite plugin system lets extension authors have full control over their kernels.&lt;/p&gt;
&lt;p&gt;A prototype for running Basthon in JupyterLite is being developed in the following repository: &lt;a href="https://framagit.org/casatir/basthon-jupyterlab/"&gt;https://framagit.org/casatir/basthon-jupyterlab/&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Basthon in JupyterLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/014-1_mMmSo4GPx7rpxbMpyf-VAA.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Basthon in JupyterLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="deploying-jupyterlite"&gt;Deploying JupyterLite&lt;/h2&gt;
&lt;p&gt;JupyterLite can easily be deployed as a static website. That’s it, no server, no complicated setup, no scalability issue. Just a plain HTTP server to serve static files to users.&lt;/p&gt;
&lt;p&gt;This simple approach makes it possible to use a variety of options: nginx, &lt;a href="https://mybinder.org"&gt;Binder,&lt;/a&gt; GitHub Pages or GitLab Pages, Vercel, Netlify, and more. It can even be deployed to ReadTheDocs, which is where the default JupyterLite demo site is hosted and continuously updated.&lt;/p&gt;
&lt;p&gt;Many of the deployment scenarios are already documented in &lt;a href="https://jupyterlite.readthedocs.io/en/latest/deploying.html"&gt;https://jupyterlite.readthedocs.io/en/latest/deploying.html&lt;/a&gt;. There is also a demo template to easily deploy a custom JupyterLite website on GitHub Pages with a single click: &lt;a href="https://github.com/jupyterlite/demo"&gt;https://github.com/jupyterlite/demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks to the work by Nicholas Bollweg in this &lt;a href="https://github.com/jupyterlite/jupyterlite/pull/147"&gt;pull request&lt;/a&gt;, JupyterLite now offers a &lt;code&gt;jupyterlite&lt;/code&gt; &lt;a href="https://pypi.org/project/jupyterlite/"&gt;command line tool&lt;/a&gt; to make custom deployments much more convenient.&lt;/p&gt;
&lt;p&gt;One of the goals of JupyterLite is to let anyone build their custom distribution with the set of plugins and extensions they would like to use. For now, it requires using the &lt;code&gt;jupyterlite&lt;/code&gt; CLI, but we can imagine having a more user-friendly way of exporting a custom JupyterLite website.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="A mock-up for the JupyterLite Exporter" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/015-1_LL_UkItjshAzsEEDB4cI2A.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;A mock-up for the JupyterLite Exporter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="a-wide-range-of-use-cases"&gt;A wide range of use cases&lt;/h2&gt;
&lt;p&gt;With the ease of deployment and the low barrier to entry, JupyterLite is an excellent fit for a wide range of use cases.&lt;/p&gt;
&lt;p&gt;In the educational space, it simplifies access to teaching materials and computing environments. Teachers and students can focus on the content of their classes without worrying about server deployments and monitoring.&lt;/p&gt;
&lt;p&gt;With JupyterLite we also hope to enable the next wave of Jupyter users and make the whole ecosystem even more accessible to newcomers and the wider community.&lt;/p&gt;
&lt;p&gt;For simpler and smaller-scale projects, it could even help reduce the load on &lt;a href="https://mybinder.org"&gt;mybinder.org&lt;/a&gt; by having a &lt;a href="https://twitter.com/fperez_org/status/1385825172230262794"&gt;“binderlite” version of JupyterLite&lt;/a&gt; deployed on a CDN.&lt;/p&gt;
&lt;h2 id="try-it-online"&gt;Try it online&lt;/h2&gt;
&lt;p&gt;JupyterLite can easily be tested in a web browser using the following link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterlite.github.io/demo"&gt;https://jupyterlite.github.io/demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jupyterlite.github.io/demo"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/001-1_CMvcTaLSAD5A-WHCtnIFwA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="try-it-locally"&gt;Try it locally&lt;/h2&gt;
&lt;p&gt;JupyterLite can also be used locally. First, install the CLI package with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install --pre jupyterlite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, to build the JupyterLite website and serve it locally:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter lite init
jupyter lite build
jupyter lite serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Check out the documentation for more information about the &lt;code&gt;jupyterlite&lt;/code&gt; command-line tool: &lt;a href="https://jupyterlite.readthedocs.io/en/latest/developer-guide.html"&gt;https://jupyterlite.readthedocs.io/en/latest/developer-guide.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next steps&lt;/h2&gt;
&lt;p&gt;JupyterLite is still under active development, with a lot of improvements planned for the next iterations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Improve tooling for authoring custom in-browser kernels, reusing the JupyterLab federated (prebuilt) extension system.&lt;/li&gt;
&lt;li&gt;Improve the package management story in Pyodide with &lt;a href="https://github.com/mamba-org/mamba"&gt;mamba&lt;/a&gt; and the &lt;a href="https://github.com/conda-forge/conda-forge.github.io/issues/1401"&gt;conda-forge infrastructure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Reuse the JupyterLite packages in other lab-based applications such as &lt;a href="https://github.com/voila-dashboards/voila"&gt;Voilà&lt;/a&gt;, &lt;a href="https://github.com/mamba-org/gator"&gt;Gator&lt;/a&gt;, and the &lt;a href="https://github.com/mamba-org/quetz-frontend"&gt;Quetz Frontend&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Provide more user-friendly tools to easily export a custom JupyterLite distribution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="getting-involved"&gt;Getting involved&lt;/h2&gt;
&lt;p&gt;JupyterLite is under active development happening in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the main repository: &lt;a href="https://github.com/jupyterlite/jupyterlite"&gt;https://github.com/jupyterlite/jupyterlite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;satellite repositories (kernels, demos) in the GitHub organization: &lt;a href="https://github.com/jupyterlite"&gt;https://github.com/jupyterlite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="about-the-author"&gt;About the Author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/jtpio"&gt;Jeremy Tuloup&lt;/a&gt; is a Scientific Software Developer at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; and a Jupyter Distinguished Contributor. Maintainer and contributor of JupyterLab, Voilà, and many projects within the Jupyter ecosystem.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;We would like to acknowledge the previous work and the contributors who have worked on exploring the idea of Python in the notebook before us: &lt;a href="https://github.com/deathbeds/jyve"&gt;Jyve&lt;/a&gt;, the &lt;a href="https://github.com/iodide-project/iodide"&gt;Iodide notebook&lt;/a&gt;, &lt;a href="https://basthon.fr/"&gt;Basthon&lt;/a&gt;, and the &lt;a href="https://github.com/jtpio/p5-notebook"&gt;p5 notebook&lt;/a&gt;. It is also worth mentioning that similar projects exist outside of the Jupyter ecosystem, such as &lt;a href="https://observablehq.com"&gt;Observable&lt;/a&gt; and the &lt;a href="https://starboard.gg"&gt;Starboard Notebook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are grateful to &lt;a href="https://github.com/bollwyvl"&gt;Nicholas Bollweg&lt;/a&gt;, &lt;a href="https://twitter.com/mad_tandon"&gt;Madhur Tandon&lt;/a&gt;, &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt; for their contributions to JupyterLite, to &lt;a href="https://twitter.com/RomanYurchak"&gt;Roman Yurchak&lt;/a&gt; and team for the work on Pyodide, Romain Casati for developing the Basthon kernel.&lt;/p&gt;
&lt;p&gt;The work on JupyterLite by Jeremy Tuloup, Madhur Tandon, and Martin Renou was funded by &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterlite-jupyter-webassembly-python/images/016-1_UY4k_mgLml9uvyo_EKMdFA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLite"/><category term="WebAssembly"/></entry></feed>