<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - kernels</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/tag-kernels.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2025-10-16T15:04:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><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>Exploring Open Access Art Using the Deno Jupyter Kernel</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/" rel="alternate"/><published>2025-04-17T15:27:00+00:00</published><updated>2025-04-17T15:27:00+00:00</updated><author><name>Trevor Manz</name></author><id>tag:jasongrout.github.io,2025-04-17:/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/</id><summary type="html">&lt;p&gt;Jupyter notebooks are the de facto standard for interactive computing and data analysis, combining code, prose, and visualizations in a…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/001-1_MJgcofpQm8Pl6hSZDvSdAw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Jupyter notebooks are the &lt;em&gt;de facto&lt;/em&gt; standard for interactive computing and data analysis, combining code, prose, and visualizations in a single document.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/manzt/deno-jupyter-blog/blob/main/post.ipynb"&gt;In fact, this post was written in a notebook!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.deno.com/"&gt;Deno&lt;/a&gt; is a simplified and secure runtime for JavaScript and TypeScript, with built-in tooling and modern defaults. Since &lt;a href="https://deno.com/blog/v1.37"&gt;v1.37&lt;/a&gt;, Deno ships with a &lt;a href="https://docs.deno.com/runtime/reference/cli/jupyter/"&gt;built-in Jupyter kernel&lt;/a&gt;, making it easy to use TypeScript for data science and interactive computing.&lt;/p&gt;
&lt;p&gt;Having worked extensively with computational notebooks (mainly in Python), I find this exciting for these reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simple setup&lt;/strong&gt; — The kernel is built into the Deno CLI, so there’s no need for additional installation. Just install Deno.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved dependency management&lt;/strong&gt; — Notebooks run like standalone scripts, and Deno’s support for importing dependencies in the code itself improves dependency management and reproducibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A unified ecosystem for interactive data analysis&lt;/strong&gt; — Jupyter supports rich HTML/CSS/JS outputs, and the JavaScript ecosystem is ideal for interactive UIs. Deno connects the kernel and frontend, making notebooks more powerful and flexible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, we’ll explore the National Gallery of Art’s Open Access dataset in Jupyter with Deno. We’ll clean and analyze the data, looking at public domain works, artists, origins, and patterns. Along the way, we’ll introduce Deno-compatible tools like &lt;a href="https://github.com/pola-rs/nodejs-polars"&gt;npm:nodejs-polars&lt;/a&gt; and &lt;a href="https://observablehq.com/plot/"&gt;npm:@observablehq/plot&lt;/a&gt;, and wrap up by adding widgets and custom displays for a more interactive experience.&lt;/p&gt;
&lt;h2 id="the-dataset"&gt;The dataset&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;National Gallery of Art (NGA)&lt;/strong&gt; &lt;a href="https://www.nga.gov/open-access-images/open-data.html"&gt;Open Data Program&lt;/a&gt; provides access to over 130,000 artworks and their creators, available &lt;a href="https://github.com/NationalGalleryOfArt/opendata/tree/main/data"&gt;on GitHub&lt;/a&gt;. This dataset includes valuable metadata such as titles, dates, artists, and classifications, all under a &lt;a href="https://creativecommons.org/public-domain/cc0/"&gt;Creative Commons 0 (CC0) license&lt;/a&gt;, meaning it’s free to use and share.&lt;/p&gt;
&lt;p&gt;The collection spans a wide variety of artworks, from sculptures to paintings, by famous artists like Mary Cassatt, M.C. Escher, Vincent van Gogh, Pablo Picasso, and Georgia O’Keeffe.&lt;/p&gt;
&lt;p&gt;However, navigating this resource is challenging. The National Gallery’s website is not very user-friendly, making it difficult to make sense of what is actually in the dataset. It’s nearly impossible to get any high-level insights, like the number of paintings, which time periods are most represented, or which artists are most prevalent–let alone make specific queries.&lt;/p&gt;
&lt;p&gt;Fortunately, the &lt;a href="https://github.com/NationalGalleryOfArt/opendata/tree/main/data"&gt;dataset is available on GitHub&lt;/a&gt; as a collection of related tables, exported as CSV files. For our analysis, we’ll focus on three key tables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;objects.csv&lt;/strong&gt; — Metadata about artworks, including titles, dates, materials, and classifications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;constituents.csv&lt;/strong&gt; — Artist details such as names, nationalities, and lifespans.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;published_images.csv&lt;/strong&gt; — Links to artwork images via the NGA’s IIIF API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll &lt;strong&gt;clean&lt;/strong&gt; and &lt;strong&gt;join&lt;/strong&gt; these tables to create a &lt;strong&gt;unified dataset&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="but-wait"&gt;&lt;strong&gt;But wait!&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;While these NGA tables are free to use, they only include &lt;em&gt;metadata&lt;/em&gt;. The actual artwork images have separate licensing, with only about half being CC0-licensed. We’ll gather this info separately to identify freely available images.&lt;/p&gt;
&lt;p&gt;Our goal is to unify the data to identify public domain images, explore subsets, and view available artworks. Maybe you’ll even find an upgrade for that default OS wallpaper.&lt;/p&gt;
&lt;h2 id="loading-and-cleaning-the-data"&gt;Loading and cleaning the data&lt;/h2&gt;
&lt;h3 id="open-access-dataset"&gt;Open Access dataset&lt;/h3&gt;
&lt;p&gt;To get started, we first need to download the data. If you’ve worked with CSVs in JavaScript before, this typically looks like a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;“fetch”&lt;/a&gt; request followed by some parsing. Here, we’re using &lt;a href="https://jsr.io/@std/csv"&gt;jsr:@std/csv&lt;/a&gt; to stream the data, which gives us an array of objects.&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="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;csv&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="s2"&gt;&amp;quot;jsr:@std/csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&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="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://github.com/NationalGalleryOfArt/opendata/raw/refs/heads/main/data/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objects.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipeThrough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TextDecoderStream&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;pipeThrough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CsvParseStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;skipFirstRow&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="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="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&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;objectid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objectid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;row.title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;beginyear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;medium&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;row.medium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visualbrowserclassification&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="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&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;&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="nt"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Saint James Major&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1310&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;medium&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tempera on panel&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;painting&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="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;objectid&amp;quot;&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="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Saint Paul and a Group of Worshippers&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1333&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;medium&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tempera on panel&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;painting&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="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;objectid&amp;quot;&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="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Saint Bernard and Saint Catherine of Alexandria with the Virgin of the Annunciation [right panel]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1387&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;medium&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tempera on poplar panel&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;painting&amp;quot;&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;If we were just plotting the data, this would work fine. But an array of objects isn’t ideal for building our dataset. The NGA data is relational and needs merging, cleaning, and restructuring. A &lt;strong&gt;DataFrame&lt;/strong&gt; is better suited for our use case, offering higher-level, efficient APIs that eliminate the need to write manipulation functions by hand.&lt;/p&gt;
&lt;p&gt;Let’s see how we can load the same dataset using a DataFrame from &lt;a href="https://github.com/pola-rs/nodejs-polars"&gt;npm:nodejs-polars&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="k"&gt;import&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="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pl&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="s2"&gt;&amp;quot;npm:nodejs-polars&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objects.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;pl.DataFrame&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="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;quoteChar&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;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;objects&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="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;beginyear&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;medium&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualbrowserclassification&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/002-1_DbMTGoODjquyHq4C2F18Zw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Here, we fetch the dataset as before, but instead of parsing the CSV ourselves, we read the text response directly with Polars to create a &lt;strong&gt;pl.DataFrame&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We then chain a &lt;strong&gt;.select&lt;/strong&gt; expression to choose and rename the columns we want. Notice how, rather than operating on each row in a loop, with Polars we work directly with &lt;em&gt;columns&lt;/em&gt;. This API allows us to express complex, efficient operations (in Rust) without materializing JavaScript objects.&lt;/p&gt;
&lt;p&gt;Let’s load our other two datasets as pl.DataFrames as well:&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;constituents.csv&lt;/strong&gt; table contains information about any person or entity associated with a piece of art, such as artists, curators, or collectors.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;constituents.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;constituents&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="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;quoteChar&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;&amp;quot;&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="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;constituentid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;forwarddisplayname&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;visualbrowsernationality&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;nationality&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="nx"&gt;constituents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/003-1_RkX0rBRpRv89jfCrgv8rZw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;published_images.csv&lt;/strong&gt; table contains additional information about the artwork’s image, including a URL for the thumbnail and mapping the objectid to the &lt;a href="https://iiif.io/"&gt;IIIF (International Image Interoperability Framework)&lt;/a&gt; for the image.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;published_images.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;publishedImages&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="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;quoteChar&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;&amp;quot;&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="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;depictstmsobjectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;uuid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;iiifthumburl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;thumburl&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="nx"&gt;publishedImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/004-1_Gk0o33-bs2AR7Ys4ajyypQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The final table, &lt;strong&gt;objects_constituents.csv&lt;/strong&gt;, represents a many-to-many relationship between artworks and the people or entities associated with them. Each artwork may have multiple people linked to it.&lt;/p&gt;
&lt;p&gt;For our analysis, we’re interested in selecting the “primary” constituent (i.e., the artist). We define the primary artist as the one with the highest &lt;strong&gt;displayorder&lt;/strong&gt; for each object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&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="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objects_constituents.csv&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objectToArtist&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="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;quoteChar&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;&amp;quot;&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="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;artist&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="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;displayorder&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="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&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="nx"&gt;first&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="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;constituentid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;objectToArtist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/005-1_kr-dapKFd1hmgpYEbG8rJA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;This is a much more advanced usage of Polars, so let’s break down what’s going on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filter&lt;/strong&gt; the table to select only rows where the role is “artist”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sort&lt;/strong&gt; the filtered data by displayorder to ensure all entries are at the top.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group&lt;/strong&gt; the rows &lt;strong&gt;by&lt;/strong&gt; objectid, ensuring one row per artwork.&lt;/li&gt;
&lt;li&gt;Get the &lt;strong&gt;first&lt;/strong&gt; in each group (i.e., artist with highest display order).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select&lt;/strong&gt; just the “objectid” and “constituentid” for joining our tables.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing this logic by hand in JavaScript would be tedius, but with Polars it’s declarative and reads like a sentence. This type of chaining is common when working with relational data.&lt;/p&gt;
&lt;h2 id="determining-whether-art-is-in-public-domain"&gt;Determining whether art is in public domain&lt;/h2&gt;
&lt;p&gt;We’ve loaded the relevant tables from the Open Access dataset, but they do not indicate whether images associated with an artwork are in the public domain. While CC0 images can be freely downloaded, shared, and repurposed, not all art with the CC0 license are &lt;em&gt;legally&lt;/em&gt; in the public domain everywhere due to local copyright laws and edge cases.&lt;/p&gt;
&lt;p&gt;Let’s find a way to add public domain information to our data set.&lt;/p&gt;
&lt;p&gt;That information is available on the NGA website, but only through the search user interface. Extracting it manually would be impractical, so I reverse-engineered an API call to retrieve the IDs of artworks with public domain images.&lt;/p&gt;
&lt;p&gt;We only need the IDs, not the full data, but the query is slow. Since this isn’t an official API, I’ve saved the results separately. There are about 50k IDs identifying public domain images in our dataset.&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;// This is not an official API, so I’ve cached the results to avoid &lt;/span&gt;
&lt;span class="c1"&gt;// repeatedly querying the server.&lt;/span&gt;
&lt;span class="c1"&gt;// &lt;/span&gt;
&lt;span class="c1"&gt;// let response = await fetch(&amp;quot;https://www.nga.gov/bin/ngaweb/collection-search-result/search.pageSize__100000.pageNumber__1.lastFacet__artobj_downloadable.json?artobj_downloadable=Image_download_available&amp;quot;); &lt;/span&gt;
&lt;span class="c1"&gt;// let data = await response.json();&lt;/span&gt;
&lt;span class="c1"&gt;// Deno.writeTextFileSync(&amp;quot;public-domain-ids.txt&amp;quot;, data.results.map((object) =&amp;gt; object.id).join(&amp;quot;\n&amp;quot;));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;publicDomainIds&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="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readTextFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public-domain-ids.txt&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="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&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="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="join-to-single-unified-table"&gt;“Join” to single unified table&lt;/h2&gt;
&lt;p&gt;Now that we have all our data cleaned and loaded, we’ll perform a large join to combine all these tables into a &lt;strong&gt;single unified table&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Again, this challenging to do manually, but with Polars, we can nicely express these complex join operations. Finally, we use a &lt;strong&gt;.isIn&lt;/strong&gt; expression to derive a new column that indicates whether a piece of art is public domain.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;df&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="nx"&gt;objects&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;objectToArtist&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;on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&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="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;constituents&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;on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;constituentid&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="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publishedImages&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;on&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&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="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;constituentid&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="nx"&gt;withColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;isIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicDomainIds&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&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="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&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="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;descending&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;nullsLast&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/006-1_pfamudvyutL613ZC_nuGJg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;With our unified dataset finally loaded, let’s move on to some plotting.&lt;/p&gt;
&lt;h2 id="exploring-art-who-created-it-when-and-more-with-plots"&gt;Exploring art, who created it, when, and more with plots&lt;/h2&gt;
&lt;p&gt;The NGA portal allows some exploration of these data but focuses on specific artworks. We’re more interested in broader insights — where most art comes from, who created it, when it was made, and whether it’s public domain.&lt;/p&gt;
&lt;p&gt;As a JavaScript/TypeScript runtime, Deno gives us access to many visualization libraries to ask these kinds of questions. We’ll use &lt;a href="https://observablehq.com/plot/"&gt;npm:@observablehq/plot&lt;/a&gt; (Observable Plot) to create plots, spot patterns, and better understand the collection.&lt;/p&gt;
&lt;p&gt;Note this post is not a tutorial on Observable Plot, so don’t worry if some of the code below is not immediately clear. The goal is to demonstrate how to use Observable Plot in Jupyter with Deno to extract insights from our data. I’ll highlight any Deno-specific details or useful tips along the way.&lt;/p&gt;
&lt;p&gt;Let’s start by examining the distribution of artworks across different “types” in the NGA collection.&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="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Plot&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="s2"&gt;&amp;quot;npm:@observablehq/plot&amp;quot;&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="nb"&gt;document&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="s2"&gt;&amp;quot;jsr:@manzt/jupyter-helper&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Convert our DataFrame to Array&amp;lt;Object&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;records&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="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toRecords&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;marginLeft&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;color&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;legend&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="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;marks&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;barY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupX&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&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="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;sort&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;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-y&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="nx"&gt;fill&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;d&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="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;public&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="s2"&gt;&amp;quot;Public domain&amp;quot;&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="s2"&gt;&amp;quot;Copyrighted&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="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="c1"&gt;// Provide a custom `document`&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;document&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;For the code above, the key things to keep in mind are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom document&lt;/strong&gt; — Observable Plot relies on the browser DOM, but since we’re in Deno, we provide a custom document from &lt;a href="https://jsr.io/@manzt/jupyter-helper"&gt;jsr:@manzt/jupyter-helper&lt;/a&gt; to enable rendering.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Converting our Polars DataFrame to records&lt;/strong&gt; — Observable Plot works best with arrays of objects, so we use &lt;strong&gt;df.toRecords()&lt;/strong&gt; to make the data easier to work with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;For specifics on the&lt;/em&gt; &lt;em&gt;&lt;strong&gt;Plot&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;API, see the&lt;/em&gt; &lt;a href="https://observablehq.com/plot/"&gt;&lt;em&gt;documentation&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href="https://observablehq.com/@observablehq/plot-gallery"&gt;&lt;em&gt;examples&lt;/em&gt;&lt;/a&gt; &lt;em&gt;available.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/007-0_iO8EF2CUzpVxl6em.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Right away, we can see that prints, drawings, and photographs make up most of the collection. Each category varies in how many images are public domain — images of prints, drawings, sculptures, and paintings are mostly public domain, while images of photographs and portfolios are largely copyrighted.&lt;/p&gt;
&lt;p&gt;We can get more granular in our plot very easily with Observable Plot, which is &lt;strong&gt;composable and expressive&lt;/strong&gt;. With just a few tweaks, you can completely change how the data is represented. For example, starting from the plot above, we can modify marks and some encoding fields and produce an entirely different chart, allowing for quick exploration and iteration — like this “waffle” bar chart.&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;marginLeft&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;color&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;legend&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="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;marks&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waffleY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="cm"&gt;/* changed, Plot.barY */&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupZ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cm"&gt;/* changed, Plot.groupX */&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;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&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="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;fill&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;d&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="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;public&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="s2"&gt;&amp;quot;Public Domain&amp;quot;&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="s2"&gt;&amp;quot;Copyrighted&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;sort&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;fx&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-y&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="cm"&gt;/* changed, sort: { x: &amp;quot;-y&amp;quot;  } */&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* new */&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="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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ruleY&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0&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="nb"&gt;document&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;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/008-0_QtnqOHV1z4f0n1Kx.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;With both Polars and Plot, we can ask more specific questions about the data, such as &lt;strong&gt;“Which artists have the most artwork in the collection?”&lt;/strong&gt; Here, we first use Polars to group the data by artist (“name”) and whether the artwork is public domain (“public”), counting the number of works for each. Then, we use Plot to visualize the top 25 artists.&lt;/p&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;artworkTotals&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="nx"&gt;df&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&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="nx"&gt;len&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="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name_count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;25&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="nx"&gt;toRecords&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;marginLeft&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;color&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;legend&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="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;marks&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;barX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artworkTotals&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;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name_count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;sort&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;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-x&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="nx"&gt;fill&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;d&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="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;public&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="s2"&gt;&amp;quot;Public domain&amp;quot;&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="s2"&gt;&amp;quot;Copyrighted&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="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;document&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;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/009-0_uT6qwM4VdbZE5jjU.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Again, we can draw some high-level takeaways from this plot: The photographer Robert Frank has nearly twice as many works as the next highest artist. Some entries, like &lt;strong&gt;“American 20th Century”&lt;/strong&gt; and &lt;strong&gt;“German 15th Century,”&lt;/strong&gt; represent groups rather than individuals. Additionally, an artist’s work is typically either &lt;strong&gt;entirely public domain or not at all&lt;/strong&gt; — likely because public domain status is determined at the &lt;strong&gt;collection level&lt;/strong&gt;, with institutions clearing entire groups of works at once rather than evaluating individual pieces separately.&lt;/p&gt;
&lt;p&gt;With our tidy and clean dataset, we can quickly iterate in Deno to ask both broad questions (“What even is &lt;em&gt;in&lt;/em&gt; this collection?”) and very specific ones (“Who created this artwork in a given year?”).&lt;/p&gt;
&lt;h2 id="deeper-exploration-with-interactivity"&gt;Deeper exploration with interactivity&lt;/h2&gt;
&lt;p&gt;So far, we’ve used &lt;strong&gt;static plots&lt;/strong&gt; to explore our data, but Jupyter notebooks support rich, interactive outputs using &lt;strong&gt;HTML, CSS, and JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With Deno, we can add more interactivity to our data exploration by either using &lt;strong&gt;off-the-shelf interactive&lt;/strong&gt; &lt;a href="https://github.com/manzt/anywidget"&gt;&lt;strong&gt;anywidgets&lt;/strong&gt;&lt;/a&gt; by quickly building &lt;strong&gt;custom components&lt;/strong&gt; for visualizing subsets in ways prebuilt tools don’t support. Deno provides direct access to the full web ecosystem, making custom visualizations seamless.&lt;/p&gt;
&lt;p&gt;In this section, we’ll look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prebuilt interactive table &lt;strong&gt;anywidgets&lt;/strong&gt; for exploring data&lt;/li&gt;
&lt;li&gt;Create custom &lt;strong&gt;&lt;Gallery /&gt;&lt;/strong&gt; component to visualize subsets of the dataset&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="exploring-data-with-dataframe-viewer-anywidgets"&gt;Exploring data with DataFrame viewer anywidgets&lt;/h2&gt;
&lt;p&gt;In Jupyter, “widgets” extend notebook outputs with interactive views and controls for objects in the kernel (i.e., backend). Unlike standard outputs, they consist of both kernel-side and front-end code, communicating directly via custom messages. While the Jupyter Widget ecosystem is largely Python-centric, the &lt;a href="https://github.com/manzt/anywidget"&gt;&lt;strong&gt;anywidget&lt;/strong&gt; project&lt;/a&gt; provides a kernel-agnostic way to create and share these components.&lt;/p&gt;
&lt;p&gt;With anywidget, widgets can be published to &lt;a href="https://jsr.io/"&gt;JSR&lt;/a&gt; and used in the Deno Jupyter kernel. For example, &lt;a href="https://jsr.io/@manzt/jupyter-helper"&gt;jsr:@manzt/jupyter-helper&lt;/a&gt; provides some off-the-shelf anywidgets for interactively viewing Polars data frames.&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;agGrid&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="s2"&gt;&amp;quot;jsr:@manzt/jupyter-helper&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;agGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;200&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// just look at the first 200 items&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/010-1_62m9scX8q8zpeMppdsV5hg.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;agGrid&lt;/strong&gt; export renders the &lt;strong&gt;pl.DataFrame&lt;/strong&gt; using the popular &lt;a href="https://www.ag-grid.com/"&gt;AG Grid&lt;/a&gt; library, allowing for interactive sorting, filtering, and pagination. This default view is minimal but can be easily extended with AG Grid’s many features.&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;quak&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="s2"&gt;&amp;quot;jsr:@manzt/jupyter-helper&amp;quot;&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;paintings&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="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;painting&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="nx"&gt;quak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paintings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/011-1_7gY1tFtpCC_4oi5SN8lAEA.mp4" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;quak&lt;/strong&gt; export renders the &lt;strong&gt;pl.DataFrame&lt;/strong&gt; with the &lt;a href="https://github.com/manzt/quak"&gt;&lt;strong&gt;quak&lt;/strong&gt;&lt;/a&gt; data table viewer. Like &lt;strong&gt;agGrid&lt;/strong&gt;, it also includes summary visualizations above each column, showing 1D distributions, and can scale to much more data (e.g., the entire collection). These 1D summary visualizations are &lt;em&gt;interactive&lt;/em&gt;, enabling &lt;em&gt;cross-filtering&lt;/em&gt; across columns.&lt;/p&gt;
&lt;p&gt;Together, the programmatic and interactive layers enable unique hybrid workflows working with data. Some queries are easier to express in code, while others are more intuitive through direct manipulation. In notebooks, the two approaches come together naturally.&lt;/p&gt;
&lt;p&gt;For example, Polars can be used to subset the data to just public domain paintings (~2k out of ~115k artworks), and then a date range can be selected interactively by &lt;em&gt;brushing&lt;/em&gt; in the UI to explore variation across other columns.&lt;/p&gt;
&lt;h2 id="creating-a-custom-component"&gt;Creating a custom &lt;strong&gt;&lt;Gallery/&gt;&lt;/strong&gt; component&lt;/h2&gt;
&lt;p&gt;Deno gives us direct access to the web ecosystem, making it easy to build small, useful UIs for exploring our data in ways that off-the-shelf tools don’t support.&lt;/p&gt;
&lt;p&gt;For example, it would be helpful to actually see a set of artwork in a “gallery view” to provide intuition about what’s in that subset. We can do that since the dataset contains image URLs.&lt;/p&gt;
&lt;p&gt;Let’s create a custom &lt;a href="https://react.dev/learn/writing-markup-with-jsx"&gt;&lt;strong&gt;JSX component&lt;/strong&gt;&lt;/a&gt; that server-side renders (SSR) a gallery view for any dataset subset using the &lt;strong&gt;render&lt;/strong&gt; export from &lt;a href="https://jsr.io/@manzt/jupyter-helper"&gt;jsr:@manzt/jupyter-helper&lt;/a&gt;. This helper simply converts JSX to an HTML string, which we display using the &lt;a href="https://docs.deno.com/api/deno/~/Deno.jupyter.Displayable"&gt;&lt;strong&gt;Deno.jupyter.Displayable&lt;/strong&gt;&lt;/a&gt; interface.&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="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;React&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="s2"&gt;&amp;quot;npm:react&amp;quot;&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;render&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="s2"&gt;&amp;quot;jsr:@manzt/jupyter-helper&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;size&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="mf"&gt;100&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="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;grid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`repeat(auto-fill, minmax(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;px, 1fr))`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;gap&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;4px&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="o"&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="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;objectid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;thumburl&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&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="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;objectid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;thumburl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;publicDomain&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;objectid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;relative&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;textAlign&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;center&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="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sb"&gt;`https://www.nga.gov/collection/art-object-page.&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;objectid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;.html`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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;display&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;block&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;relative&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="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;thumburl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;100%&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;objectFit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;cover&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;borderRadius&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;5px&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="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &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="nx"&gt;publicDomain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;https://mirrors.creativecommons.org/presskit/icons/zero.svg&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Public Domain&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;absolute&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;3px&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;3px&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;20px&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;20px&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;opacity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;0.60&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="o"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/a&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/div&amp;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;&lt;strong&gt;Gallery&lt;/strong&gt; is a custom component that displays a grid of artwork thumbnails from a given dataset. Each image &lt;strong&gt;links to its NGA collection page&lt;/strong&gt;, and public domain works are marked with a CC0 icon.&lt;/p&gt;
&lt;p&gt;We can SSR &lt;strong&gt;Gallery&lt;/strong&gt; with our data to better understand a smaller subset, making it easier to explore specific artists, time periods, or categories visually. For example, &lt;strong&gt;a random sample&lt;/strong&gt; from all the collection:&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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/012-0_-_2C_srE0EGp-0HE.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Or sample of just paintings:&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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;paintings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/013-0_RFL3Dapu3QIwpN2P.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;It’s worth taking a moment to appreciate that in just a few lines of code, we’ve built a &lt;strong&gt;domain-specific utility&lt;/strong&gt; that makes our data more intuitive to explore. This kind of flexibility is powerful and unique to Deno, letting us quickly craft custom utilities that provide deeper insights.&lt;/p&gt;
&lt;p&gt;With our toolkit, let’s take a closer look at the NGA dataset:&lt;/p&gt;
&lt;h2 id="drilling-down-on-when-the-art-was-created"&gt;Drilling down on &lt;em&gt;when&lt;/em&gt; the art was created&lt;/h2&gt;
&lt;p&gt;So far, our data exploration has focused on &lt;strong&gt;high-level summaries&lt;/strong&gt; of the NGA collection by artist and artwork type using static plots. Now, let’s drill down into something potentially more interesting using both our complete toolkit: &lt;em&gt;when&lt;/em&gt; these works were created.&lt;/p&gt;
&lt;p&gt;We’ll start with a stacked histogram to see the overall distribution of artwork types over time, then facet by public domain status to uncover any patterns.&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;y&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;grid&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="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;color&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;legend&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="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;marks&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rectY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&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="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1401&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 15th century or later&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;binX&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&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="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;x&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;d&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="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;fy&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;d&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="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;public&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="s2"&gt;&amp;quot;Public Domain&amp;quot;&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="s2"&gt;&amp;quot;Copyrighted&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="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ruleY&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;0&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;marginLeft&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;marginRight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;document&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;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/014-0_WU1i-imsUsRnWiTe.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The distribution of artworks with &lt;strong&gt;copyrighted&lt;/strong&gt; and &lt;strong&gt;public domain&lt;/strong&gt; images are noticeably different. Nearly all &lt;strong&gt;copyrighted works appear after 1850&lt;/strong&gt;, while &lt;strong&gt;public domain artworks are more evenly spread&lt;/strong&gt; over time — &lt;em&gt;except&lt;/em&gt; for a &lt;strong&gt;sharp spike in public domain drawings from the 1940s&lt;/strong&gt;, with over 15k artworks added.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is a striking anomaly&lt;/strong&gt;. Are they all from the same artist? Why only drawings? Let’s take a closer look.&lt;/p&gt;
&lt;p&gt;We’ll filter down the public domain data to this span.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;notablePeriod&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="nx"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// just public domain&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1925&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// between 1925 - 1955&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1955&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;y&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;grid&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="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;color&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;legend&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="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;marks&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;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rectY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;notablePeriod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toRecords&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;binX&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&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="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;x&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;d&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="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&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="p"&gt;)],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;document&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;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/015-0_wa5EYkPYIidClyna.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Zooming into the &lt;strong&gt;1925–1955 range&lt;/strong&gt;, we see that almost all the data falls within a much narrower window. Interestingly, the spike is highly concentrated between &lt;strong&gt;1935 and 1942&lt;/strong&gt;, with very little artwork outside this period.&lt;/p&gt;
&lt;p&gt;Narrowing in closer, we can see that in this window there are ~18k individual drawings:&lt;/p&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;totalNumberOfDrawings&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="nx"&gt;notablePeriod&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1934&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="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1943&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="nx"&gt;shape&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;totalNumberOfDrawings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 18096&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;made by over 1000 individuals:&lt;/p&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;numberOfArtists&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="nx"&gt;notablePeriod&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1934&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="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;year&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1943&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="nx"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&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="nx"&gt;len&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="nx"&gt;shape&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;numberOfArtists&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1034&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This gets even more interesting! The artwork is not from just one specific source — there was a &lt;strong&gt;surge of public works between 1935 and 1942&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let’s use our custom &lt;strong&gt;&lt;Gallery /&gt;&lt;/strong&gt; view to explore a subset of these works — maybe seeing them will give us some insight.&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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Gallery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;notablePeriod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2025/exploring-open-access-art-using-the-deno-jupyter-kernel/images/016-0_u40CvXdHxGXBjEOp.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;So all these artworks, despite being from different individuals, have a very similar style and medium. That’s something worth digging into — what might explain this pattern?&lt;/p&gt;
&lt;p&gt;Thanks to our custom &lt;strong&gt;&lt;Gallery /&gt;&lt;/strong&gt; component, we can click on artworks and view their metadata. Sampling a few, we see that they all belong to the &lt;a href="https://www.nga.gov/features/exhibitions/outliers-and-american-vanguard-artist-biographies/index-of-american-design.html"&gt;“Index of American Design”&lt;/a&gt; collection.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Conceived as an effort to document and preserve American folk and decorative arts, the Index of American Design consists of &lt;strong&gt;18,257&lt;/strong&gt; watercolor renderings created between &lt;strong&gt;1935 and 1942&lt;/strong&gt; as part of a Federal Art Project (FAP) work-relief program. Around 400 artists meticulously recreated textiles, woodcarvings, weathervanes, and other objects from across the U.S., aiming to establish a distinct American visual lineage. The project was ultimately housed at the National Gallery of Art, becoming a widely exhibited visual archive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s it! We found a &lt;strong&gt;large&lt;/strong&gt; &lt;strong&gt;collection of public works funded by a federal program during the Great Depression&lt;/strong&gt; — clear in both the data and historical context.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using Deno’s Jupyter kernel, we explored the NGA Open Access dataset, combining static plots, interactive widgets, and custom JSX components to uncover patterns in the data. This led us to a historical insight — the Index of American Design, a public art project from the 1930s.&lt;/p&gt;
&lt;p&gt;With Deno, JSX, and anywidget, we combine data analysis with interactive web-based visualization, making it easy to build domain-specific tools for deeper exploration.&lt;/p&gt;
</content><category term="JavaScript"/><category term="kernels"/></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>Meet Xeus-R: a future-proof Jupyter kernel for R</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/" rel="alternate"/><published>2024-01-22T17:20:00+00:00</published><updated>2024-01-22T17:20:00+00:00</updated><author><name>Romain François</name></author><id>tag:jasongrout.github.io,2024-01-22:/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/</id><summary type="html">&lt;p&gt;The Jupyter ecosystem provides a versatile and user-friendly platform for data analysis and scientific computing, it has democratised…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;img alt="Screenshot of a Jupyter notebook with two cells displayed alongside a contextual help panel. The first cell shows the execution of an R command display(head(mtcars, 3)). The output is a nicely formatted table display with 3 rows. The second cell contains is a call to the ggplot function from the ggplot2 package. The output is a scatter plot of the variables hwy and displ, coloured by the class factor. The cursor shows auto completion for ggplot. On the right, contextual help for ggplot." src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/001-1_cFmKZKFycjvG1v_Xub4snQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLab session with a notebook powered by the Xeus-R kernel for R&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;em&gt;Jupyter&lt;/em&gt; ecosystem provides a versatile and user-friendly platform for data analysis and scientific computing, it has democratised access to powerful tools for researchers, scientists, educators, and data professionals.&lt;/p&gt;
&lt;p&gt;The name &lt;em&gt;Jupyter&lt;/em&gt;, a blend of &lt;em&gt;Julia&lt;/em&gt;, &lt;em&gt;Python&lt;/em&gt;, and &lt;em&gt;R&lt;/em&gt; has always hinted at its relevance in multiple scientific programming languages and has since conquered new horizons thanks to its flexible and extensible design.&lt;/p&gt;
&lt;p&gt;Underneath the interactive surface of &lt;em&gt;Jupyter&lt;/em&gt; lie the &lt;em&gt;Jupyter&lt;/em&gt; kernels, a set of language specific engines orchestrating the dynamic execution and exploration of code. Kernels implement the &lt;em&gt;Jupyter&lt;/em&gt; protocol, a shared specification of the communication process between notebooks and kernels, defining how messages, commands and outputs are exchanged and processed.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Xeus&lt;/em&gt; framework is a central piece of the puzzle, offering a reference to facilitate &lt;em&gt;Jupyter&lt;/em&gt; kernel implementations. The speed, extensibility, and alignment with the official protocol make &lt;em&gt;Xeus&lt;/em&gt; a perfect technology to implement new kernels.&lt;/p&gt;
&lt;h2 id="xeus-r"&gt;Xeus-R&lt;/h2&gt;
&lt;p&gt;Today, we, &lt;em&gt;a collaborative team led by Romain François and supported by QuantStack&lt;/em&gt;, are thrilled to announce the initial release of &lt;em&gt;Xeus-R&lt;/em&gt;, a future-proof &lt;em&gt;Jupyter&lt;/em&gt; kernel for R. &lt;em&gt;Xeus-R&lt;/em&gt; builds upon the components of &lt;em&gt;Xeus&lt;/em&gt; to facilitate the use of &lt;em&gt;R&lt;/em&gt; in &lt;em&gt;Jupyter&lt;/em&gt; notebooks and already supports typical capabilities: &lt;em&gt;R&lt;/em&gt; code execution, inspection, completion, and contextual help.&lt;/p&gt;
&lt;p&gt;The recent advancements in support for &lt;em&gt;WebAssembly&lt;/em&gt; in the &lt;em&gt;R&lt;/em&gt; world led by the &lt;em&gt;WebR&lt;/em&gt; project, the flexible design of Xeus, and the rise of &lt;em&gt;JupyterLite&lt;/em&gt; all contribute to our strong belief that &lt;em&gt;Xeus-R&lt;/em&gt; has the potential to deliver &lt;em&gt;Jupyter&lt;/em&gt; in-browser experience for data scientists using &lt;em&gt;R&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Xeus-R&lt;/em&gt; is currently at the stage of a proof of concept, but we wanted to communicate about it early to broadcast our excitement about its potential, share some of our thoughts, and initiate a conversation.&lt;/p&gt;
&lt;h2 id="relationship-with-irkernel"&gt;Relationship with IRkernel&lt;/h2&gt;
&lt;p&gt;While the &lt;em&gt;IRkernel&lt;/em&gt; project paved the way and is the current reference implementation of a &lt;em&gt;Jupyter&lt;/em&gt; kernel for R, it does not take advantage of the &lt;em&gt;Xeus&lt;/em&gt; infrastructure and includes a complete implementation of the &lt;em&gt;Jupyter&lt;/em&gt; protocol, down to the composition of all of the &lt;em&gt;JSON&lt;/em&gt; messages and most importantly how they are transported through &lt;em&gt;ZeroMQ&lt;/em&gt; sockets.&lt;/p&gt;
&lt;p&gt;In contrast, &lt;em&gt;Xeus-R&lt;/em&gt; fully embraces and leverages the capabilities of &lt;em&gt;Xeus&lt;/em&gt;, while borrowing some higher level concepts from &lt;em&gt;IRkernel&lt;/em&gt; and reusing features from its dependent packages &lt;em&gt;IRdisplay&lt;/em&gt; and &lt;em&gt;repr&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We believe this path will benefit future development as it gives us the freedom to work on innovations that are specific to the needs of &lt;em&gt;R&lt;/em&gt; users, and let &lt;em&gt;Xeus&lt;/em&gt; absorb evolutions of the &lt;em&gt;Jupyter&lt;/em&gt; protocol.&lt;/p&gt;
&lt;h2 id="key-features"&gt;Key features&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Xeus-R&lt;/em&gt; implements the basic building blocks of what you would expect from a &lt;em&gt;Jupyter&lt;/em&gt; kernel. The results of a code cell, i.e. the value of its last expression are displayed in the output, taking advantage of &lt;em&gt;ANSI&lt;/em&gt; escape codes for colours as were made popular by the &lt;em&gt;crayon&lt;/em&gt; and later &lt;em&gt;cli&lt;/em&gt; &lt;em&gt;R&lt;/em&gt; packages.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/002-1_XznRckf13HpNy_S7h7-_iA.webp" alt="Screenshot of a Jupyter notebook with 3 cells visible. The first cell [2] contains a line of R code that uses cat and glue to print “hello world” with “world” coloured in green thanks the col_green function from the cli package. The second cell [3] shows the command head(mtcars) which outputs a plain text version of the first 6 rows of the mtcars data frame. The third cell [9] contains R code that loads the tibble package and then prints mtcars as a tibble, therefore benefitting from formatting." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Traditional and &lt;em&gt;ggplot2&lt;/em&gt;-powered graphics are captured and displayed as images. The aspect ratio of plots can be controlled by setting the &lt;em&gt;repr.plot.height&lt;/em&gt; and &lt;em&gt;repr.plot.width&lt;/em&gt; options either globally, or locally via the &lt;em&gt;cell_options()&lt;/em&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/003-1_oi1bSbjYZg_I_H104v-75g.webp" alt="A Jupyter notebook cell [13] with R code and its output. The code sets options locally with the cell_options() function and then creates a box plot with the following code: boxplot(decrease ~ treatment, data = OrchardSprays, col = “bisque”, log = “y”). The output is a a graphic with 8 boxplots, representing the treatment factor and its levels A to H on the x axis, and the variable decrease on the y axis, with a logarithmic scale." loading="lazy" data-body-image=""&gt;&lt;br&gt;
&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/004-1_7bZITs9PxXYWwqgDWEniJg.webp" alt="Screenshot of a Jupyter notebook cell [15] with R code and output. The code is loading the ggplot2 package and then creating a scatterplot with the following code: ggplot(mpg, aes(displ, hwy, colour = class)) + geom_point(). The resulting plot is a scatter plot with displ on the x axis and fwy on the y axis, showing different colored points representing different classes of vehicles from the class factor." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Thanks to the scaffolding in place in the &lt;em&gt;IRdisplay&lt;/em&gt; and &lt;em&gt;repr&lt;/em&gt; packages, &lt;em&gt;Xeus-R&lt;/em&gt; is also able to emit richer displays through mime bundles.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/005-1_-T33jRshu16eWh96_OVuvA.webp" alt="Screenshot of a Jupyter notebook with 2 cells with R code. The first cell [16] contains the command display(1:10). The output shows a rich representation of 1:10: the numbers 1 to 10 separated by dots. The second cell [17] also uses the display function to create a rich display of the first 6 rows of the mtcars data frame: display(head(mtcars)). The result presents the data frame as a well formatted table display with alternating shading for rows to improve readability." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;In contrast with &lt;em&gt;IRkernel&lt;/em&gt;, the rich displays need to be explicitly requested with the &lt;em&gt;display()&lt;/em&gt; function. There are cases however, such as &lt;em&gt;HTML&lt;/em&gt; widgets, where automatic rich display makes the most sense because a text/plain representation is not valuable.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/006-1_5QOitnpbBdDNpQ9ERIILpg.webp" alt="Screenshot of a Jupyter notebook cell [24] showing R code that uses packages htmltools and plotly to create an HTML widget with 2 plotly-powered histograms. The result first shows information messages about conflicts, then shows two typical plotly-powered histograms. The top histogram shows a selection, indicating that the display is about to be zoomed in a smaller value range, to show that the HTML widget can be interacted with." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Xeus-R&lt;/em&gt; also supports contextual help and includes a basic completion system, based on the internal &lt;em&gt;utils::.getTokenFromLine()&lt;/em&gt; system.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2024/meet-xeus-r-a-future-proof-jupyter-kernel-for-r/images/007-1_jWuiihVGV0_7utFx3Tzvng.webp" alt="Screenshot of a Jupyter notebook interface with an open R code cell and a contextual help panel side by side. On the left side, the code cell is active with the incomplete R function call “rnorm(“ being typed, and an auto-completion tooltip showing the options “n=”, “mean=”, “sd=”. The tooltip also shows a repetition of the function name “rnorm”. On the right side, the contextual help panel shows the documentation for the rnorm function." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="installation-and-getting-started"&gt;Installation and Getting Started&lt;/h2&gt;
&lt;p&gt;At the time of writing this article, &lt;em&gt;Xeus-R&lt;/em&gt; is only available and tested to work on Unix-based systems. We consciously decided to focus initial efforts on features rather than platform comprehensiveness, but we should be able to fix this soon enough.&lt;/p&gt;
&lt;p&gt;The easiest way to get up and running with &lt;em&gt;Xeus-R&lt;/em&gt; is to install it from conda-forge:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mamba&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;xeus-r
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instructions to build from source are available in the github repository for &lt;em&gt;Xeus-R&lt;/em&gt;: &lt;a href="https://github.com/jupyter-xeus/xeus-r"&gt;https://github.com/jupyter-xeus/xeus-r&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="looking-ahead-jupyterlite-integration"&gt;Looking ahead: JupyterLite Integration&lt;/h2&gt;
&lt;p&gt;Another benefit of a &lt;em&gt;Xeus&lt;/em&gt;-based approach is that it has been designed in a way that supports in-browser experience to power &lt;em&gt;JupyterLite&lt;/em&gt;. Together with the recent developments around &lt;em&gt;WebR&lt;/em&gt;, we can envision &lt;em&gt;R&lt;/em&gt; becoming a first-class citizen in the &lt;em&gt;JupyterLite&lt;/em&gt; world.&lt;/p&gt;
&lt;p&gt;This is an exciting vision and will serve as our North Star ⭐️ for future developments of &lt;em&gt;Xeus-R&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Join us in welcoming &lt;em&gt;Xeus-R&lt;/em&gt;, a &lt;em&gt;Jupyter&lt;/em&gt; kernel for &lt;em&gt;R&lt;/em&gt; that leverages the robust foundation of &lt;em&gt;Xeus&lt;/em&gt; architecture. &lt;em&gt;R&lt;/em&gt; is a language of paramount importance for Data Science and Scientific Computing and it rightfully deserves its place as a first-class citizen within the &lt;em&gt;Jupyter&lt;/em&gt; ecosystem.&lt;/p&gt;
&lt;p&gt;We firmly believe that &lt;em&gt;Xeus-R&lt;/em&gt; has the potential to become a reference implementation for an &lt;em&gt;R&lt;/em&gt; kernel, well positioned to tackle the challenges of the ever-evolving landscape of in-browser Scientific Computing, a promising future led by the &lt;em&gt;JupyterLite&lt;/em&gt; and &lt;em&gt;WebR&lt;/em&gt; projects.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The work by Romain François on Xeus-R was funded by &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="author-bio"&gt;Author Bio&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.linkedin.com/in/romain-francois"&gt;Romain François&lt;/a&gt; is a prominent figure in the &lt;em&gt;R&lt;/em&gt; community, recognised for his longstanding contributions to &lt;em&gt;R&lt;/em&gt; programming and Data Analysis. With a profound passion for open-source software and commitment to advancing the capabilities of &lt;em&gt;R&lt;/em&gt;, he has earned a well-deserved reputation as a respected expert.&lt;/p&gt;
&lt;p&gt;Romain’s expertise encompasses a wide spectrum of &lt;em&gt;R&lt;/em&gt;-related fields, including package development, data manipulation, and interoperability with other programming languages. He is widely recognized for his work on packages like &lt;em&gt;dplyr&lt;/em&gt;, which has become a cornerstone for efficient data manipulation in &lt;em&gt;R&lt;/em&gt;, and for his contributions to &lt;em&gt;Rcpp&lt;/em&gt;, a library that bridges the power of &lt;em&gt;C++&lt;/em&gt; with &lt;em&gt;R&lt;/em&gt; for optimizing &lt;em&gt;R&lt;/em&gt; packages and enhancing performance.&lt;/p&gt;
</content><category term="kernels"/><category term="xeus"/></entry><entry><title>Plug your application into the Jupyter world</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/" rel="alternate"/><published>2023-10-23T06:09:00+00:00</published><updated>2023-10-23T06:09:00+00:00</updated><author><name>David Brochart</name></author><id>tag:jasongrout.github.io,2023-10-23:/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/</id><summary type="html">&lt;p&gt;Kernels are a simple but powerful abstraction in the Jupyter architecture. They encapsulate language interpreters and make them accessible…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/001-1_ENzq7i52UtqxESs4UTKBYw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Kernels are a simple but powerful abstraction in the Jupyter architecture. They encapsulate language interpreters and make them accessible through a standardized interface. This is the key to Jupyter’s remarkable versatility, with over 100 supported languages. Embedding a kernel in your custom application can seamlessly expose it to the Jupyter ecosystem. Not only can you attach JupyterLab to your program for e.g. state inspection, as you would do with a debugger, but you can truly extend your application with all the power of the Jupyter ecosystem.&lt;/p&gt;
&lt;h2 id="applications-gui-and-api"&gt;Applications: GUI and API&lt;/h2&gt;
&lt;p&gt;Several applications such as &lt;a href="https://www.freecad.org/"&gt;FreeCAD&lt;/a&gt;, &lt;a href="https://docs.blender.org/api/current/"&gt;Blender&lt;/a&gt;, or &lt;a href="https://docs.qgis.org/3.28/en/docs/pyqgis_developer_cookbook/index.html"&gt;QGIS&lt;/a&gt; provide a Python API to access their functionalities programmatically, and include an embedded Python console which allows using this API from within the application. Users can both use the graphical user interface and operate on the application in a scripted fashion depending on their needs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Python consoles in FreeCAD, Blender, and QGIS (from left to right)." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/002-1_EMYG_XFNokXREwsUa9pbFA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Python consoles in FreeCAD, Blender, and QGIS (from left to right).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you think about it, these are the kinds of workflows that Jupyter has always been about, but Jupyter has evolved in the opposite direction. Instead of adding scripting to GUIs, Jupyter added GUIs to scripting. At first, Jupyter was IPython, a console for interactive computing. Then it went to the Web and became IPython Notebook, renamed to Jupyter Notebook. With the browser it became possible to display richer content and create small GUIs, using Jupyter Widgets. Then JupyterLab was created and allowed to create fully-fledged desktop-like applications in the browser, like 3-D CAD modelers.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterLab showing notebooks using JupyterCAD and ipyleaflet." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/003-1_m4WwlNYiIvMeZu1eXhrl7Q.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterLab showing notebooks using JupyterCAD and ipyleaflet.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Jupyter put a lot of attention on building a good experience interacting with the Python REPL, which is not the main focus of domain-specific desktop applications. The Python console embedded in desktop applications is often quite raw. Allowing users to connect from Jupyter can enable a much better experience.&lt;/p&gt;
&lt;p&gt;What if there was a bridge that allowed you to connect both worlds, your application on one side and Jupyter on the other side? What is the common denominator allowing code to interact with an application, while also being shared with Jupyter? In the Jupyter infrastructure, we call it a kernel.&lt;/p&gt;
&lt;h2 id="jupyter-kernels"&gt;Jupyter kernels&lt;/h2&gt;
&lt;p&gt;Kernels provide a standardized way to expose language interpreters to applications, in a language-agnostic fashion. You can think of it as a server that accepts requests such as “execute this piece of code” and that replies with a result. The result is actually a data representation, it can be as simple as “3” for an input of “1+2”, or a complex interactive plot. A kernel usually lives in a separate process on your machine, but it can also run in the same process, or even remotely on another machine. Someone wanting to connect to this kernel will need the connection information such as the IP address and the ports of the different communication “channels”. It is stored in what is called a connection file.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Jupyter kernel protocol allows clients to interact with different languages." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/004-1_ywYEVpCKlbL6aBEGERsXSw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Jupyter kernel protocol allows clients to interact with different languages.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Anyone who has access to this connection file can then execute code in the kernel. This is “shared code”, meaning that every client to this kernel eventually runs code in the same interpreter. If one client sets a variable “a=1”, other clients will see this variable. They are all running the “same program”. If the kernel runs in the same process as the application, then the kernel can interact directly with it. This way of sharing the execution engine serves as a bridge between the application and Jupyter.&lt;/p&gt;
&lt;p&gt;Now it is just a matter of exposing the connection file so that e.g. JupyterLab can see it. The application launching the kernel can save the connection file to a specific directory, and give the path to JupyterLab. And that’s it, JupyterLab can run a notebook using this kernel, allowing it to interact with the application.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyter-xeus/xeus-qt-python"&gt;xeus-qt-python&lt;/a&gt; repository is a great place to start and see how to embed a Python kernel inside a Qt application. Under the hood, it uses &lt;a href="https://github.com/jupyter-xeus/xeus-qt"&gt;xeus-qt&lt;/a&gt;, an integration of the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; kernel architecture in the Qt event loop. The Qt application actually runs JupyterLab in a Qt WebEngine window. Users can run code in a notebook and interact with the application, which in turn can interact with the notebook. Here, we create a Qt button from a notebook. When clicking on the button, a callback prints a message in the notebook. This demonstrates a two-way binding between the Qt application and JupyterLab.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Example of a Qt application embedding JupyterLab." src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/005-1_FTlpril_YV5W6-xYelNrtA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Example of a Qt application embedding JupyterLab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;SlicerJupyter&lt;/a&gt; from Kitware was one of the first applications to take advantage of this approach. You can read more in this &lt;a href="/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/"&gt;blog post&lt;/a&gt;. We hope that this technology will unlock a new brand of use-cases and allow applications to reach a new audience through the Jupyter ecosystem. Please reach out to us if you need help!&lt;/p&gt;
&lt;h3 id="about-the-author"&gt;About the author&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/plug-your-application-into-the-jupyter-world/images/006-1_gGWqElhixsQA-cGOzWcb7w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/davidbrochart"&gt;&lt;strong&gt;David Brochart&lt;/strong&gt;&lt;/a&gt; is a technical director at QuantStack. He mostly works on the Jupyter backend stack. He contributed to the development of real-time collaboration in JupyterLab, and created the &lt;a href="https://davidbrochart.github.io/jupyverse/"&gt;Jupyverse&lt;/a&gt; project, a next-generation implementation of the Jupyter server.&lt;/p&gt;
&lt;h3 id="acknowledgments"&gt;Acknowledgments&lt;/h3&gt;
&lt;p&gt;This work was made possible thanks to several contributors. &lt;a href="https://twitter.com/mari_meir"&gt;&lt;strong&gt;Mariana Meireles&lt;/strong&gt;&lt;/a&gt; was the initial author of &lt;a href="https://github.com/jupyter-xeus/xeus-qt"&gt;xeus-qt&lt;/a&gt;. &lt;a href="https://twitter.com/ThorstenBeier"&gt;&lt;strong&gt;Thorsten Beier&lt;/strong&gt;&lt;/a&gt; is a scientific software developer at QuantStack and contributes to the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; kernel ecosystem. He is the initial creator of the &lt;a href="https://github.com/jupyter-xeus/xeus-qt-python"&gt;xeus-qt-python&lt;/a&gt; demonstration. &lt;a href="https://twitter.com/JohanMabille"&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt;&lt;/a&gt; is a technical director at QuantStack and he is the main author of &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt;. &lt;strong&gt;Afshin Darian&lt;/strong&gt; is a technical director at QuantStack and worked on the UI in JupyterLab 4.0 allowing to use an external kernel.&lt;/p&gt;
</content><category term="kernels"/></entry><entry><title>A Jupyter kernel for GNU Octave</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/" rel="alternate"/><published>2023-01-11T17:33:00+00:00</published><updated>2023-01-11T17:33:00+00:00</updated><author><name>Giulio Girardi</name></author><id>tag:jasongrout.github.io,2023-01-11:/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/</id><summary type="html">&lt;p&gt;Today, we are happy to announce the xeus-octave project, a Jupyter kernel for GNU Octave.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/001-0__HZJpXD-awr4mPDG.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;GNU Octave is a long-standing member of the scientific computing ecosystem, featuring a mathematics-oriented syntax with built-in 2D/3D plotting and visualization tools, and compatible with the Matlab syntax. It is a natural candidate for high-quality integration in the Jupyter ecosystem.&lt;/p&gt;
&lt;p&gt;Jupyter has been devised with language agnosticism in mind since it was spun off from the IPython project. The Kernel (the part of the infrastructure responsible for executing the code input by the user) is a key extension point to Jupyter, and dozens of language kernels exist in the ecosystem.&lt;/p&gt;
&lt;p&gt;Prior art on Jupyter / GNU Octave integration includes the &lt;a href="https://github.com/Calysto/octave_kernel"&gt;Calysto/Octave_kernel&lt;/a&gt; project by Steven Silvester (who is also a core Jupyter maintainer and the co-creator of JupyterLab). This existing kernel is part of the family of kernels built on top of the &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt; reference implementation of the Jupyter protocol, in Python.&lt;/p&gt;
&lt;p&gt;Today, we are happy to announce the &lt;a href="https://github.com/jupyter-xeus/xeus-octave/"&gt;xeus-octave&lt;/a&gt; project, a Jupyter kernel for GNU Octave. Xeus-octave was created by &lt;a href="http://rapgenic.it/"&gt;Giulio Girardi&lt;/a&gt;, recently joined by &lt;a href="https://twitter.com/AntoineProuvost"&gt;Antoine Prouvost&lt;/a&gt; — and has been incorporated into the Project Jupyter governance.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/002-0__YEF9gl4rxnapBiz.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Xeus-octave is built upon the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; library, a C++ implementation of the Jupyter kernel protocol that enables fast development of new kernels using programming languages’ native APIs, unlocking new possibilities without parsing standard outputs or requiring a Python interpreter at runtime. After all, a Jupyter kernel is merely an executable providing a well-defined communication protocol. It is not bound to Python APIs.&lt;/p&gt;
&lt;h2 id="a-fully-featured-kernel"&gt;A fully-featured kernel&lt;/h2&gt;
&lt;p&gt;We strove to make the xeus-octave kernel as complete as possible for this first iteration.&lt;/p&gt;
&lt;h2 id="multiple-graphical-toolkits"&gt;Multiple graphical toolkits&lt;/h2&gt;
&lt;h3 id="notebook"&gt;Notebook&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;notebook&lt;/code&gt; Octave graphical toolkit can be used to render plots natively, and present the figure as an image in the notebook.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screenshot of GNU Octave in action in Jupyter with the native Octave plots" src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/003-0_0tfvTOYDrjvK6SyO.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The classic Octave sombrero plot rendered as an image inside the notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="plotly"&gt;Plotly&lt;/h3&gt;
&lt;p&gt;An experimental &lt;code&gt;plotly&lt;/code&gt; Octave graphical toolkit can also use &lt;a href="https://plotly.com/"&gt;Plotly&lt;/a&gt; to render code in the browser, with dynamic views, zooms, and hover tooltips.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screenshot of GNU Octave in action in Jupyter with the Plotly plots" src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/004-0_ZT86_erLnKUyRvP4.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Multiple plots rendered in a single plotly figure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="rich-display"&gt;Rich display&lt;/h2&gt;
&lt;p&gt;The kernel binds with the C++ interface of GNU Octave to provide a complete Octave experience. Having access to the exact internal type of the objects, xeus-octave can present data with rich output.&lt;/p&gt;
&lt;p&gt;Matrices can be presented as HTML or LaTeX tables.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/005-0_015CLZOO-yfxSRSe.webp" alt="Screenshot of GNU Octave in action in Jupyter with the rich rendering of matrices" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Symbolic expressions can be presented as LaTeX formulas.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/006-0_U-Nxc_9FqhxARiGF.webp" alt="Screenshot of GNU Octave in action in Jupyter with the rich rendering of equations" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Structured data can be visualized as interactive tree views&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/007-0_oshl__rdb1-OtfCM.webp" alt="Screenshot of GNU Octave in action in Jupyter with the rich rendering of Octave structured data as interactive tree views." loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="interactive-help"&gt;Interactive help&lt;/h2&gt;
&lt;p&gt;Formatted help, extracted from the GNU Octave reference manual, can be queried using the &lt;code&gt;?&lt;/code&gt; syntax.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/008-0_zQcXBUjgbwqY0IHG.webp" alt="Screenshot of GNU Octave in action in Jupyter with the interactive help displayed in an output cell" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="trying-it-online"&gt;Trying it online&lt;/h2&gt;
&lt;p&gt;You can try the new Jupyter kernel for GNU Octave by clicking on the link below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-octave/stable?urlpath=/lab/tree/notebooks/xeus-octave.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/009-0_SDB4IuVwEGuk2yz1.webp" alt="Logo of Binder" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Binder linked above includes examples of notebooks in the fields of Electronics, demonstrating the usability of xeus-octave in real-life complex scenarios.&lt;/p&gt;
&lt;p&gt;This interactive demo is also featured on the Jupyter website at &lt;a href="https://jupyter.org/try#kernels"&gt;https://jupyter.org/try#kernels&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="installing-xeus-octave"&gt;Installing xeus-octave&lt;/h2&gt;
&lt;p&gt;Xeus-octave has been packaged on conda-forge and can be installed with mamba or conda.&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;mamba&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;xeus-octave&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="future-work"&gt;Future Work&lt;/h2&gt;
&lt;p&gt;In future iterations, we will work towards consolidating the xeus-octave experience, by adding cell magics and improving the Plotly integration.&lt;/p&gt;
&lt;p&gt;A major ongoing development is the implementation of an Octave backend for Jupyter interactive widgets, based on the &lt;a href="https://github.com/jupyter-xeus/xwidgets"&gt;xwidgets&lt;/a&gt; project. Combined with &lt;a href="https://github.com/voila-dashboards/voila"&gt;Voilà&lt;/a&gt;, it will be possible to use GNU Octave to create standalone web dashboards.&lt;/p&gt;
&lt;p&gt;Another ongoing endeavor is to port a growing portion of the scientific computing stack to WebAssembly (using the emscripten-forge project). Producing a Wasm build of GNU Octave will enable its use with &lt;a href="https://github.com/jupyterlite/jupyterlite"&gt;Jupyterlite&lt;/a&gt;, enabling large-scale deployments on websites, blogs, and arbitrary web pages without any need for scalable cloud infrastructure.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;&lt;strong&gt;About the authors&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/010-1_h3ZtdyQicGecxGFPkc3tYQ.jpeg" alt="Headshot of Antoine Prouvost" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/AntoineProuvost"&gt;Antoine Prouvost&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; and a xeus-octave maintainer. Previously, Antoine was researching Machine Learning and Combinatorial Optimization at the &lt;a href="https://twitter.com/DS4DM"&gt;DS4DM&lt;/a&gt; research chair and &lt;a href="https://twitter.com/MILAMontreal"&gt;Mila&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/a-jupyter-kernel-for-gnu-octave/images/011-1_vUpNORkYEpszm-vTZoSXwg.jpeg" alt="Headshot of Giulio Girardi" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://rapgenic.it/"&gt;Giulio Girardi&lt;/a&gt; is a Master’s degree student in Electronic Engineering at the &lt;a href="https://twitter.com/UniPadova"&gt;UNIPD&lt;/a&gt;, currently working as an Electronic Engineer at &lt;a href="http://protechgroup.it/"&gt;Protech Engineering&lt;/a&gt;. Giulio is the creator and a maintainer of the xeus-octave project.&lt;/p&gt;
</content><category term="kernels"/></entry><entry><title>Xeus-Lite</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/" rel="alternate"/><published>2021-10-25T09:40:00+00:00</published><updated>2021-10-25T09:40:00+00:00</updated><author><name>Thorsten Beier</name></author><id>tag:jasongrout.github.io,2021-10-25:/medium-archive/pelican/posts/2021/xeus-lite/</id><summary type="html">&lt;p&gt;JupyterLite + Xeus = ❤️️&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/001-1_PUlJi6czJovO90t-Dsgt5g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;JupyterLite is a &lt;a href="https://jupyter.org/"&gt;JupyterLab&lt;/a&gt; distribution that runs entirely in the web browser, backed by in-browser language kernels. &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; is C++ library for writing &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; kernels. In this blogpost we show how these two can work together.&lt;/p&gt;
&lt;h2 id="jupyterlite"&gt;JupyterLite&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/002-1_dbJO26hiSR8EFygX1rnqrA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;With vanilla Jupyter, kernels are running in separate processes. The communication between the kernels and the client is achieved with &lt;a href="https://zeromq.org/"&gt;ZeroMQ&lt;/a&gt;. In contrast to this, JupyterLite runs entirely in the browser, including the language kernels. The following kernels are already implemented in the JupyterLite framework:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript Kernel&lt;/li&gt;
&lt;li&gt;P5 Kernel: Since P5 is a JavaScript library, it is easy to provide a P5 kernel running in the browser.&lt;/li&gt;
&lt;li&gt;A Python kernel based on &lt;a href="https://github.com/pyodide/pyodide"&gt;pyodide&lt;/a&gt; called pyolite: While the two JavaScript based kernels are trivial to implement as in-browser kernels, &lt;a href="https://github.com/pyodide/pyodide"&gt;pyodide&lt;/a&gt; is a Python interpreter compiled to WebAssembly via Emscripten. Not only Python itself has been compiled to WebAssembly, but also the whole Scientific Stack including packages like: Numpy, Scipy, Pandas and many more. An overview of packages can be found &lt;a href="https://github.com/pyodide/pyodide/tree/main/packages"&gt;here&lt;/a&gt;. Furthermore any pure Python package can be installed from within pyolite.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since JupyterLite runs entirely in the browser, it is &lt;a href="https://jupyterlite.github.io/demo/lab/index.html"&gt;very easy to try it out&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id="xeus"&gt;Xeus&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://xeus.readthedocs.io/en/latest/?badge=latest"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/003-1_c-fyt4I1GOXnrq3tkexaGA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; is C++ library for writing &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; kernels. It is not a kernel but a library that simplifies the process of writing kernels. &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; implements the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;Jupyter messaging protocol&lt;/a&gt;, so that kernel authors only need to implement a handful of methods related to the actual interpreter, like executing pieces of code and auto-completing.&lt;br&gt;
With the help of Xeus, it is very easy to write Jupyter kernels . In fact, &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; has already been used for making several &lt;a href="https://jupyter.org/"&gt;l&lt;/a&gt;anguage kernels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt; is a Xeus-based C++ kernel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;xeus-python&lt;/a&gt; is a Xeus-based Python kernel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; is a Xeus-based Lua kernel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-sql"&gt;xeus-sql&lt;/a&gt; / &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite"&gt;xeus-sqlite&lt;/a&gt; are Xeus-based kernel for SQL / SQLite databases.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-robot"&gt;xeus-robot&lt;/a&gt; is a Jupyter kernel for the &lt;a href="https://robotframework.org/"&gt;Robot Framework&lt;/a&gt; .&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/lfortran/lfortran/-/merge_requests/514"&gt;LFortran&lt;/a&gt; is a modern interactive Fortran compiler with a Xeus-based Jupyter kernel.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since it is very easy to write Xeus based kernels for Jupyter, we also want to leverage Xeus for writing JupyterLite kernels. This has been the main motivation for &lt;a href="/posts/2021/xeus-2-0/"&gt;Xeus 2.0&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="xeus-architecture"&gt;Xeus Architecture&lt;/h2&gt;
&lt;p&gt;Xeus is has three extension points as depicted below&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/004-1_CTgb-wk-_TMWVhDzB-Yx7g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;&lt;strong&gt;Server&lt;/strong&gt;&lt;/em&gt; is the middleware component responsible for receiving and sending messages to the Jupyter client. The default server it is built upon ZeroMQ and handles the concurrency model of the application.&lt;br&gt;
As we will see later, a custom server is needed to connect Xeus with JupyterLite.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;&lt;strong&gt;Interpreter&lt;/strong&gt;&lt;/em&gt; provides the interface that kernel authors must implement.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;&lt;strong&gt;Debugger&lt;/strong&gt;&lt;/em&gt; provides the interface that can to be implemented when the kernel shall have &lt;a href="/posts/2020/a-visual-debugger-for-jupyter/"&gt;debug capabilities&lt;/a&gt;, but we do not yet support this for Xeus-JupyterLite-Kernels.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jupyterlite-xeus-kernels"&gt;JupyterLite Xeus-Kernels&lt;/h2&gt;
&lt;p&gt;Traditionally, the Jupyter kernel and the Client to the Jupyter kernel run in separate processes as depicted below:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/005-1_jHLLHjTjNWlV0e38GjvACw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The communication between the kernels and the client (ie the Jupyter-server) are done with inter-process communication via &lt;a href="https://zeromq.org/"&gt;ZeroMQ&lt;/a&gt;. But with JupyterLite the &lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;kernel runs directly in the browser&lt;/a&gt;. In that case, ZeroMQ is not required anymore to communicate with the frontend. This was the motivation for &lt;a href="/posts/2021/xeus-2-0/"&gt;Xeus 2.0&lt;/a&gt;, where it is possible to build Xeus without ZeroMQ. To implement JupyterLite Kernels with Xeus, we need to implement a custom Server which handles the &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt;/&lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html"&gt;Embind&lt;/a&gt;-based communication layer&lt;/p&gt;
&lt;p&gt;The overall architecture is best described with the picture below: The C++ Xeus-Kernel is compiled to WebAssembly with &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt; / &lt;a href="https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html"&gt;Embind&lt;/a&gt;. The WebAssembly compiled code runs in a dedicated WebWorker thread. The WebWorker communicates via Message Passing with a thin layer of TypeScript which implements the &lt;a href="https://github.com/jupyterlite/jupyterlite/blob/d94d472fcdfb5ce760820a6a9cd2c6ea6fc84014/packages/kernel/src/tokens.ts#L45"&gt;IKernel&lt;/a&gt; API of JupyterLite. The communication between JupyterLite and the JupyterLite k ernel is done entirely in JavaScript.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/006-1_AwkYyHKDxj0_EYitMAxZ5Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The C++ code needed to compile an existing xeus-kernel to WebAssembly is very minimal:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/007-1_Qdv9gVF_oO9n2nLffY35SQ.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;Furthermore one needs a thin layer of TypeScript / JavaScript to implement the JupyterLite &lt;a href="https://github.com/jupyterlite/jupyterlite/blob/d94d472fcdfb5ce760820a6a9cd2c6ea6fc84014/packages/kernel/src/tokens.ts#L45"&gt;IKernel API&lt;/a&gt;. A good example of this code is given in the &lt;a href="https://github.com/jupyterlite/xeus-lua-kernel"&gt;JupyterLite Xeus-Lua repository&lt;/a&gt;. While most of the repositories code is boilerplate, one needs to provide a simple &lt;a href="https://github.com/DerThorsten/jupyterlite_xeus_lua/blob/main/Dockerfile"&gt;Dockerfile&lt;/a&gt; which compiles the Xeus-Kernel to WebAssembly.&lt;/p&gt;
&lt;h2 id="xeus-lua"&gt;Xeus-Lua&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/008-1_XjZ84G9Ftb3krekpiqBuiw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We recently released a &lt;a href="/posts/2021/from-jupyter-to-the-moon/"&gt;Xeus based Lua kernel.&lt;/a&gt; Even though its worthwhile to have Xeus-Lua kernel for vanilla Jupyter, the main purpose of Xeus-Lua was to experiment with Xeus-based JupyterLite kernels.&lt;br&gt;
We choose Lua as the first language to try out Xeus-based JupyterLite kernels since Lua is easy to compile to WebAssembly.&lt;br&gt;
A JupyterLite instance with Xeus-Lua included can be found &lt;a href="https://jupyterlite.github.io/demo/lab/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Xeus-Lua has built in support for interactive widgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/009-1_Iwg6iPSZmr5o1ZozOlJXDQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Xeus-Lua has built in support for interactive widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="xeus-wren"&gt;Xeus-Wren&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/DerThorsten/xeus-wren"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/010-1_AaO7Or9i6ZDmYR3PNGXh9Q.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wren is relatively new “&lt;a href="https://wren.io/"&gt;Classy Little scripting Language&lt;/a&gt;” created by the author of the great book &lt;a href="https://craftinginterpreters.com/"&gt;“Crafting Interpreters”&lt;/a&gt;. Wren can be described with the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://wren.io/"&gt;Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in a familiar, modern syntax.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We choose Wren as a second language to experiment with Xeus + JupyterLite since it is small, simple to compile to WebAssembly and easy to embed in C++. A JupyterLite instance with Xeus-Wren included can be found &lt;a href="https://jupyterlite.github.io/demo/lab?path=xeus-wren%2Fiwren.ipynb"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Xeus-Wren has built in support for displaying data of various MIME-types" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/xeus-lite/images/011-1_b-X9cmzXfq7NIXeEwfEzow.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Xeus-Wren has built in support for displaying data of various MIME-types&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="outlook"&gt;Outlook&lt;/h2&gt;
&lt;p&gt;There are many languages which can be compiled to WebAssembly like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Julia: &lt;a href="https://github.com/Keno/julia-wasm"&gt;https://github.com/Keno/julia-wasm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Octave: &lt;a href="https://news.ycombinator.com/item?id=22926120"&gt;https://news.ycombinator.com/item?id=22926120&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;C++: &lt;a href="https://github.com/binji/wasm-clang"&gt;https://github.com/binji/wasm-clang&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Python: &lt;a href="https://github.com/pyodide/pyodide"&gt;https://github.com/pyodide/pyodide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also databases as sqlite can be compiled to WebAssembly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sqlite: &lt;a href="https://github.com/sql-js/sql.js/"&gt;https://github.com/sql-js/sql.js/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our goal is to provide Xeus-based JupyterLite kernels for these projects to make the JupyterLite ecosystem even more powerful.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;We like to thank&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/jtpio"&gt;Jeremy Tuloup&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;, and &lt;a href="https://github.com/bollwyvl"&gt;Nicholas Bollweg&lt;/a&gt; for their work on JupyterLite.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/johanmabille"&gt;Johan Mabille&lt;/a&gt; for Xeus and his help implementing the Xeus-Emscripten server.&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/thorstenbeier"&gt;Thorsten Beier&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://quantstack.net/"&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/DerThorsten/nifty"&gt;nifty&lt;/a&gt; and &lt;a href="https://github.com/ukoethe/vigra"&gt;vigra&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; and &lt;a href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt; in Python.&lt;/p&gt;
</content><category term="kernels"/><category term="WebAssembly"/><category term="xeus"/></entry><entry><title>From Jupyter to the Moon</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/" rel="alternate"/><published>2021-09-24T12:24:00+00:00</published><updated>2021-09-24T12:24:00+00:00</updated><author><name>Thorsten Beier</name></author><id>tag:jasongrout.github.io,2021-09-24:/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/</id><summary type="html">&lt;p&gt;A new Jupyter kernel for Lua based on Xeus&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/001-1_Or2bu8twFwywgACqLOE9AA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter architecture in a nutshell" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/002-0_jvG9j3sKufQRiuLl.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; architecture in a nutshell&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A key principle in Jupyter’s design is &lt;em&gt;&lt;strong&gt;language agnosticism&lt;/strong&gt;&lt;/em&gt;, and one of the main extension points of the Jupyter ecosystem is the &lt;em&gt;&lt;strong&gt;kernel&lt;/strong&gt;&lt;/em&gt;, the part of the architecture responsible for executing the user’s code.&lt;/p&gt;
&lt;p&gt;In the end, a Jupyter kernel is merely an executable implementing a well-defined inter-process communication protocol.&lt;/p&gt;
&lt;p&gt;A reference implementation of the kernel is ipykernel, which provides the default Python kernel, and utilities to build other language kernels. Another reference implementation of the protocol is &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="xeus"&gt;Xeus&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; is C++ library for writing &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; kernels. It is not a kernel but a library that simplifies the process of writing kernels. &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; implements the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;Jupyter messaging protocol&lt;/a&gt;, so that kernel authors only need to implement a handful of methods related to the actual interpreter, like executing pieces of code and auto-completing. There is a long &lt;a href="https://github.com/dbohdan/embedded-scripting-languages"&gt;list of scripting languages&lt;/a&gt; which can be embedded in C/C++. With the help of Xeus, it is very easy to write kernels for such languages. In fact, &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt; has already been used for making several &lt;a href="https://jupyter.org/"&gt;l&lt;/a&gt;anguage kernels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt; is a Xeus-based C++ kernel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;xeus-python&lt;/a&gt; is a Xeus-based Python kernel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-sql"&gt;xeus-sql&lt;/a&gt; / &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite"&gt;xeus-sqlite&lt;/a&gt; are Xeus-based kernel for SQL / SQLite databases.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-robot"&gt;xeus-robot&lt;/a&gt; is a Jupyter kernel for the &lt;a href="https://robotframework.org/"&gt;Robot Framework&lt;/a&gt; .&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/lfortran/lfortran/-/merge_requests/514"&gt;LFortran&lt;/a&gt; is a modern interactive Fortran compiler with a Xeus-based Jupyter kernel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="xwidgets"&gt;xwidgets&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyter-xeus/xwidgets"&gt;xwidget&lt;/a&gt; library is a C++ implementation of the &lt;a href="https://ipywidgets.readthedocs.io/en/latest/"&gt;Jupyter Interactive Widget protocol&lt;/a&gt;. Xwidgets were initially developed for xeus-cling, the Xeus C++ kernel, but any Xeus-based kernel can reuse these components.&lt;/p&gt;
&lt;h2 id="a-xeus-based-lua-kernel"&gt;A xeus-based Lua kernel&lt;/h2&gt;
&lt;p&gt;Today, I am pleased to announce the release of &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt;, a new &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; kernel for &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; based on &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/003-0_xQYvimlGgIZuuZC2.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h2 id="lua"&gt;Lua&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt;, which means “Moon” in Portuguese, is a lightweight embeddable scripting language written in C. While Python is one of the leading scripting languages for scientific application, &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; is the leading scripting language used in games / game engines. The &lt;a href="https://en.wikipedia.org/wiki/Category:Lua_(programming_language)-scripted_video_games"&gt;list of video games using Lua&lt;/a&gt; is long and includes triple-a games like Crisys and Farcry. Not only games rely on Lua, but &lt;a href="https://en.wikipedia.org/wiki/List_of_applications_using_Lua"&gt;also applications like Adobe Photoshop Lightroom&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="sol2"&gt;Sol2&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="With the Sol2 library, Lua can be embedded into C++ in a very elegant way." src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/004-0_qDdOEdX0PhOKNTMJ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;With the &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt; library, Lua can be embedded into C++ in a very elegant way.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While Lua itself can be embedded in a C++ application quite easy, it is even easier when using &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt;. &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt; is a C++ library that simplifies the usage of Lua from C++. Furthermore &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt; can be used to expose C++ classes to Lua, in a similar fashion as &lt;a href="https://github.com/pybind/pybind11"&gt;pybind11&lt;/a&gt; can be used to expose C++ classes to Python.&lt;/p&gt;
&lt;h2 id="xeus-lua"&gt;Xeus-lua&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="The compulsory “Hello, World!” from xeus-lua. This blog-post would be incomplete without it." src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/005-0_Rw3rs6Cdj5FEfAQf.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The compulsory “Hello, World!” from xeus-lua. This blog-post` would be incomplete without it.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I am pleased to announce &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua,&lt;/a&gt; a new Jupyter kernel for the Lua language based on &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt;. Using &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt; and &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt;, it was very easy to implement a feature rich kernel using modern C++. With the help of &lt;a href="https://github.com/ThePhD/sol2"&gt;Sol2&lt;/a&gt;, it was trivial to expose the xwidgets classes to Lua such that we can use the widgets within &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; notebooks.&lt;/p&gt;
&lt;h2 id="but-why-a-lua-kernel"&gt;But why a Lua kernel?&lt;/h2&gt;
&lt;p&gt;While &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; might not be the hippest language in 2021, there are multiple reasons while I choose to implement such a &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; kernel, and why such a kernel can be useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing a &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;Xeus&lt;/a&gt;-based kernel is the best way to learn and familiarize oneself with the &lt;a href="https://github.com/jupyter-xeus"&gt;xeus ecosystem&lt;/a&gt;. And since Lua is so easy to build and embed, Lua and Xeus are perfect match. Implementing this kernel gave me great insights into Xeus and Jupyter itself.&lt;/li&gt;
&lt;li&gt;Learning &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; with &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; is fun! Before writing &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt;, I had zero Lua experience. While implementing xeus-lua I learned &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; from within Jupyter by playing around with xwidgets from within Lua — this was a very joyful experience.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; implementation might serve as reference for future kernel authors. In particular the &lt;a href="https://github.com/jupyter-xeus/xeus-lua/blob/main/src/xwidgets.cpp"&gt;integration of xwidgets in xeus-lua&lt;/a&gt; might be useful for future implementations.&lt;/li&gt;
&lt;li&gt;Since Lua is so easy to build, &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; can be built for WebAssembly and will be integrated in &lt;a href="https://jupyterlite.readthedocs.io/en/latest/"&gt;JupyterLite&lt;/a&gt; — &lt;strong&gt;stay tuned!&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="features"&gt;Features&lt;/h2&gt;
&lt;p&gt;Obviously &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; supports the execution of code.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Simple code execution" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/006-0_AzW845V1EKlvxO2j.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Simple code execution&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Streams are redirected to the notebooks output&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Redirection of streams" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/007-0_A-2SpNknQua_D73o.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Redirection of streams&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Errors messages are shown in the notebooks output&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Error Handling" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/008-0_yMSCAFt70y43daKm.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Error Handling&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Input streams are redirected to the notebooks input system&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="User Input" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/009-0_Pfa9ASycXFejuZKp.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;User Input&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Statements can be auto-completed with a press of tab&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Code Completion" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/010-0_dQfGwr6PtuQaSUAb.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Code Completion&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Data of different sources as Latex, JSON and html can be displayed.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Display Data" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/011-0_tHhXDPzcThHTqor5.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Display Data&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Interactive widgets can be added to the notebook!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Widgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/012-0_066MCiSNqv21zJOS.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We can feed the widgets with data!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Binary buffers for widgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/013-0_ArP2iU5tKYDHvvZd.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Binary buffers for widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Try It Out&lt;/p&gt;
&lt;p&gt;You can try it right now with &lt;a href="https://mybinder.org/"&gt;binder&lt;/a&gt;, without the need of installing anything on your computer, just by clicking on &lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-lua/main?urlpath=/lab/tree/notebooks/game_of_life.ipynb"&gt;&lt;strong&gt;this link&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-lua/main?urlpath=/lab/tree/notebooks/game_of_life.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/from-jupyter-to-the-moon/images/014-0_NiFI9An8ZGVDSYyG.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="other-lua-kernels"&gt;Other Lua kernels&lt;/h2&gt;
&lt;p&gt;There are several &lt;a href="https://www.lua.org/"&gt;Lua&lt;/a&gt; kernels available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tomstitt/lupyter"&gt;lupyter&lt;/a&gt;: A Python based implementation of a Lua Jupyter kernel. The heavy lifting is done in a C-extension.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/guysv/ilua"&gt;ILua&lt;/a&gt;: A feature rich implementation. Ilua is implemented in Python and does the communication with Lua via IPC.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pakozm/IPyLua"&gt;IPyLua&lt;/a&gt;: IPyLua is a pure Lua implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While these implementations are feature rich and work well, none of the above supports interactive widgets. Since &lt;a href="https://github.com/jupyter-xeus/xeus-lua"&gt;xeus-lua&lt;/a&gt; is based on X&lt;a href="https://github.com/jupyter-xeus/xeus"&gt;eus&lt;/a&gt;, we can reuse components from the Xeus ecosystem like &lt;a href="https://github.com/jupyter-xeus/xwidgets"&gt;xwidgets&lt;/a&gt; with minimal effort.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;We are grateful to &lt;a href="https://twitter.com/juliettetaka?lang=en"&gt;Juliette Taka&lt;/a&gt; for the illustration.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the Author&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://quantstack.net/thorsten.html"&gt;Thorsten Beier&lt;/a&gt; is a Scientific Software Engineer at &lt;a href="https://quantstack.net/"&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/DerThorsten/nifty"&gt;nifty&lt;/a&gt;, and &lt;a href="https://github.com/ukoethe/vigra"&gt;vigra&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; and &lt;a href="https://www.ilastik.org/"&gt;ilastik&lt;/a&gt; in Python.&lt;/p&gt;
</content><category term="kernels"/><category term="xeus"/></entry><entry><title>Enabling the JupyterLab debugger with ipykernel</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/enabling-the-jupyterlab-debugger-with-ipykernel/" rel="alternate"/><published>2021-05-13T11:45:00+00:00</published><updated>2021-06-02T14:59:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2021-05-13:/medium-archive/pelican/posts/2021/enabling-the-jupyterlab-debugger-with-ipykernel/</id><summary type="html">&lt;p&gt;Support for the Jupyter Debugger Protocol just landed in ipykernel&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;Support for the Jupyter Debugger Protocol just landed in ipykernel&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;JupyterLab 3.0 includes a visual debugger that allows to interactively set breakpoints, step into functions, and inspect variables with any Jupyter kernel that implements the Jupyter debugger protocol.&lt;/p&gt;
&lt;p&gt;The first two language kernels to implement the new protocol were &lt;a href="https://github.com/jupyter-xeus/xeus-python/"&gt;xeus-python&lt;/a&gt; (a Python kernel) and &lt;a href="https://github.com/jupyter-xeus/xeus-robot/"&gt;xeus-robot&lt;/a&gt; (a kernel for Robot Framework). Unfortunately, the reference Python kernel, &lt;a href="https://github.com/ipython/ipykernel"&gt;&lt;strong&gt;ipykernel&lt;/strong&gt;&lt;/a&gt;, did not support debugging yet, &lt;em&gt;until now&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;Today, we are pleased to announce that debugging support landed in ipykernel, and will be available in the next major release, ipykernel 6.0. Pre-releases including the ipykernel debugger are available.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Debugging with ipykernel" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/enabling-the-jupyterlab-debugger-with-ipykernel/images/001-1_jSQWLvCYoV-L-kp_lTkRKg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Debugging with ipykernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="what-will-change-with-ipykernel-60"&gt;What will change with ipykernel 6.0?&lt;/h2&gt;
&lt;p&gt;Enabling support for debugging in ipykernel required important changes in the code base regarding the concurrency model of the kernel. The main change is that the processing of messages on the “control channel” now happens in a different thread, allowing for the processing to happen while user code is running.&lt;/p&gt;
&lt;p&gt;Ipykernel 6.0 includes several other updates. Tornado coroutines were dropped in favor of native coroutines. The Matplotlib inline backend was split into a separate package, and ipykernel depends on &lt;a href="https://github.com/microsoft/debugpy"&gt;debugpy&lt;/a&gt;, an implementation of the Debug Adapter Protocol for Python.&lt;/p&gt;
&lt;p&gt;If you are interested in testing out the new features, check out the beta release!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install ipykernel --pre
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="try-it-now"&gt;Try it now!&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href="https://mybinder.org/"&gt;MyBinder&lt;/a&gt;, you can also try it out without the need of installing anything on your computer. Just follow this link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/SylvainCorlay/a6405bbdfa9d58a670a67f3a47741bd2/HEAD?urlpath=doc%2Ftree%2Fdebugger.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/enabling-the-jupyterlab-debugger-with-ipykernel/images/002-0_rELNpt0w5qbk_GQn.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="what-about-the-future"&gt;What about the future?&lt;/h2&gt;
&lt;p&gt;A lot of new features are in the works with the JupyterLab debugger.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JupyterLab 3.1 will include several usability improvements to the debugger.&lt;/li&gt;
&lt;li&gt;It will also add the ability to submit code for execution when stopped at a breakpoint.&lt;/li&gt;
&lt;li&gt;We are working on a richer variable explorer, using Jupyter’s rich display system to enable the rich-rendering of variables in the explorer, to &lt;em&gt;e.g.&lt;/em&gt; render dataframes as tables.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, we also plan on adding debugging support to other language kernels.&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The work of Johan and Sylvain at &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt; on the debugger support in ipykernel was funded by &lt;a href="https://www.twosigma.com/"&gt;Two Sigma&lt;/a&gt;. We are grateful to Min Ragan Kelley and Matthias Bussonnier, who reviewed the pull requests on debugger support.&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/2021/enabling-the-jupyterlab-debugger-with-ipykernel/images/003-1_8_HgQuq5_HXhLXSdfGnhrA.jpeg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/JohanMabille"&gt;Johan Mabille&lt;/a&gt; is a scientific software developer at QuantStack.&lt;/p&gt;
&lt;p&gt;Johan is very active in the Jupyter ecosystem, as the creator of &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt;, a C++ implementation of the Jupyter protocol, and several language kernels, such as &lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;xeus-python&lt;/a&gt;, &lt;a href="https://github.com/jupyter-xeus/xeus-cling"&gt;xeus-cling&lt;/a&gt;, and &lt;a href="https://github.com/jupyter-xeus/xeus-robot"&gt;xeus-robot&lt;/a&gt;. Johan also made contributions to the Jupyter widgets ecosystem and to JupyterLab.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/enabling-the-jupyterlab-debugger-with-ipykernel/images/004-1_LpuIpGQIDYMhv5IBthmwcA.jpeg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/SylvainCorlay"&gt;Sylvain Corlay&lt;/a&gt; is the founder and CEO of QuantStack.&lt;/p&gt;
&lt;p&gt;As an open-source developer, Sylvain is very active in the Jupyter project with contributions in several components of the stack, including widgets, kernels, nbconvert, and others. He is also a steering committee member of the project.&lt;/p&gt;
&lt;p&gt;Sylvain also does volunteer work for the community, as member of board of directors of NumFOCUS, co-organizer of the &lt;a href="https://www.meetup.com/pyData-paris"&gt;PyData Paris Meetup&lt;/a&gt;, and vice-chair of JupyterCon 2020.&lt;/p&gt;
</content><category term="IPython"/><category term="JupyterLab"/><category term="kernels"/></entry><entry><title>Abracadabra! Bringing the magics to xeus-python</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/" rel="alternate"/><published>2021-02-18T13:30:00+00:00</published><updated>2021-02-18T14:06:00+00:00</updated><author><name>Martin Renou</name></author><id>tag:jasongrout.github.io,2021-02-18:/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/</id><summary type="html">&lt;p&gt;Last year, we set ourselves to implement a visual debugger for JupyterLab. This endeavor required major developments in the JupyterLab…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Last year, we set ourselves to implement a &lt;strong&gt;visual debugger for JupyterLab&lt;/strong&gt;. This endeavor required major developments in the JupyterLab front-end, in core-Jupyter protocols, and on the kernel side (the part of the Jupyter infrastructure responsible for executing the code).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/001-0_B0kK-zNJr0Suisyv.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For kernel-side development, we decided to start with the &lt;a href="/posts/2019/a-new-python-kernel-for-jupyter/"&gt;&lt;strong&gt;xeus-python&lt;/strong&gt; kernel&lt;/a&gt;, a lightweight implementation of a Jupyter kernel for the Python programming language. Based on &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;&lt;strong&gt;xeus&lt;/strong&gt;&lt;/a&gt;, xeus-python has a flexible concurrency model that was required to &lt;em&gt;e.g.&lt;/em&gt; add breakpoints while executing code.&lt;/p&gt;
&lt;p&gt;Unfortunately, xeus-python did not provide all ipykernel features (magics, Matplotlib support, &lt;em&gt;etc.&lt;/em&gt;). Furthermore, many notebooks depend on IPython, as they import it explicitely or make use of the IPython configuration system.&lt;/p&gt;
&lt;p&gt;Today, we are proud to announce that xeus-python supports 100% of the IPython magics! This was achieved by leveraging the core IPython package. This is getting us closer to feature parity with ipykernel.&lt;/p&gt;
&lt;h2 id="magics"&gt;Magics&lt;/h2&gt;
&lt;p&gt;xeus-python now supports all magics that IPython provides and even user-defined magics!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Support for builtin IPython magics and user-defined magics" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/002-1_PTiGVyDsazc_ziz6Bk4EnA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Support for builtin IPython magics and user-defined magics&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="matplotlib"&gt;Matplotlib&lt;/h2&gt;
&lt;p&gt;Most Matplotlib back-ends are now supported by xeus-python, allowing you to show static plots with the inline back-end or interactive plots with &lt;a href="https://github.com/matplotlib/ipympl"&gt;ipympl&lt;/a&gt; in your Notebook:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Matplotlib inline back-end and ipympl interactive back-end" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/003-1_cmnPUKlhHHwUf8PZhNQx2g.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Matplotlib inline back-end and ipympl interactive back-end&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="extending"&gt;Extending&lt;/h2&gt;
&lt;p&gt;IPython has a rich configuration system that you can tweak in order to get an enhanced Notebook experience. Xeus-python now supports any configuration you setup for IPython:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Automatically import NumPy in the IPython settings" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/004-1_bB5hiXIbVAlV3kX9KJfLfg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Automatically import NumPy in the IPython settings&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="visual-debugging"&gt;Visual Debugging&lt;/h2&gt;
&lt;p&gt;The latest JupyterLab version introduced a visual debugger in its interface. xeus-python was the first Jupyter kernel to add support for it!&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Stepping into Python code in JupyterLab with the visual debugger" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/005-1_efZAtaJQqeirAZoEqbxvdw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Stepping into Python code in JupyterLab with the visual debugger&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;xeus-python is not the only Jupyter kernel that supports debugging anymore: &lt;a href="https://github.com/jupyter-xeus/xeus-robot"&gt;xeus-robot&lt;/a&gt; is a xeus-based Jupyter kernel for the &lt;a href="https://robotframework.org/"&gt;RobotFramework&lt;/a&gt; language that supports the JupyterLab visual debugger.&lt;/p&gt;
&lt;h2 id="try-it-online"&gt;Try it online&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a href="https://mybinder.org/"&gt;MyBinder&lt;/a&gt;, you can try it out without the need of installing anything on your computer. Just follow this link:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-python/stable?urlpath=/lab/tree/notebooks/xeus-python.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/006-0_2sBOFFsk_322apCe.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;You can install the latest xeus-python version using mamba or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mamba install xeus-python -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install xeus-python -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also compile it yourself, instructions can be found in the repository:&lt;br&gt;
&lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;https://github.com/jupyter-xeus/xeus-python&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;My work on &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt; and &lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt; 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://www.techatbloomberg.com/"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the Author&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/abracadabra-bringing-the-magics-to-xeus-python/images/007-0_5XdymjYwzZe-hvy8.jpg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;My name is &lt;a href="https://twitter.com/martinRenou"&gt;Martin Renou&lt;/a&gt;, I am a Scientific Software Engineer at &lt;a href="https://quantstack.net/"&gt;&lt;strong&gt;QuantStack&lt;/strong&gt;&lt;/a&gt;. Before joining QuantStack, I studied at the aerospace engineering school &lt;a href="https://www.isae-supaero.fr/en"&gt;SUPAERO&lt;/a&gt; in Toulouse, France. I also worked at Logilab in Paris, France and Enthought in Cambridge, UK. As an open-source developer at QuantStack, I work 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/ipyleaflet"&gt;ipyleaflet&lt;/a&gt; and &lt;a href="https://github.com/bqplot/bqplot"&gt;bqplot&lt;/a&gt; in Python and JavaScript/TypeScript.&lt;/p&gt;
</content><category term="IPython"/><category term="kernels"/><category term="xeus"/></entry><entry><title>An SQL solution for Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/" rel="alternate"/><published>2021-02-03T12:03:00+00:00</published><updated>2023-01-10T18:33:00+00:00</updated><author><name>Mariana Meireles</name></author><id>tag:jasongrout.github.io,2021-02-03:/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/</id><summary type="html">&lt;p&gt;xeus-sql: A xeus-sqlite sequel&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A few months ago we released &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite"&gt;xeus-sqlite&lt;/a&gt;, a Jupyter kernel that allows users to make SQLite queries directly from the notebook. With the needs of the Jupyter data science community in mind we decided to expand on this project.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="RDBMSes supported by xeus-sql" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/001-1_tbfa_AWoqb-yB1Tk6Uq3ng.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;RDBMSes supported by xeus-sql&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Today, we’re happy to announce the next step of this endeavour, bringing you &lt;a href="https://github.com/jupyter-xeus/xeus-sql"&gt;&lt;strong&gt;xeus-sql&lt;/strong&gt;&lt;/a&gt; a general-purpose database access tool for Jupyter, which allows you to make SQL queries on many different databases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;SQLite3&lt;/li&gt;
&lt;li&gt;DB2&lt;/li&gt;
&lt;li&gt;Oracle&lt;/li&gt;
&lt;li&gt;Firebird&lt;/li&gt;
&lt;li&gt;and any database with an ODBC driver.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To offer all these integrations we rely on the &lt;a href="http://soci.sourceforge.net/"&gt;SOCI&lt;/a&gt; library as the backbone of the project. SOCI abstracts all the different database connections and query details behind a unified C++ API. xeus-sql uses SOCI and &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; to expose SQL functionalities to Jupyter.&lt;/p&gt;
&lt;p&gt;Xeus-sql, as well as the MySQL, PostgreSQL and SQLite3 backends of SOCI are available on conda-forge, meaning you can install them easily with &lt;strong&gt;conda&lt;/strong&gt; or &lt;a href="https://github.com/mamba-org/mamba"&gt;&lt;strong&gt;mamba&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# Installing xeus-sql and the mysql backend
mamba install xeus-sql soci-mysql -c conda-forge
&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;# Installing xeus-sql and the postgresql backend
mamba install xeus-sql soci-postgresql -c conda-forge
&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;# Installing xeus-sql and the sqlite backend
mamba install xeus-sql soci-sqlite -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are idiosyncrasies between these different SQL backends which we detail in the xeus-sql &lt;a href="http://xeus-sql.readthedocs.io/en/latest/"&gt;documentation&lt;/a&gt; and &lt;a href="https://github.com/jupyter-xeus/xeus-sql/tree/master/examples"&gt;examples&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="visualizing-your-queries"&gt;Visualizing your queries&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="HTML and text table visualizations for different Jupyter interfaces" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/002-1_1cl12TkS8uqKDgXvZCJQtQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;HTML and text table visualizations for different Jupyter interfaces&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You’re probably familiar with visualizing your SQL tables and query results in the form of… tables, of course. Jupyter’s rich display system offers the option of showing them in rich-text display or plain-text display depending on which application you’re using.&lt;/p&gt;
&lt;p&gt;Beyond displaying queries with tables, you can now easily create &lt;a href="https://vega.github.io/vega-lite/"&gt;Vega-Lite&lt;/a&gt; graphs from your query results directly in the notebook:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Running a series of examples on a notebook" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/003-1_pyLtQiOV5uzGk-VKAVyD4Q.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Running a series of examples on a notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To support this feature xeus-sql relies on &lt;a href="https://github.com/Quantstack/xvega"&gt;xvega&lt;/a&gt;, a C++ backend to vega, and custom Jupyter magics for plotting through a mini-language implemented in the &lt;a href="https://github.com/jupyter-xeus/xvega-bindings/"&gt;xvega-bindings&lt;/a&gt; utility library. Besides using the mini-language you can directly provide the JSON specification for your visualization, thanks to contributions by &lt;a href="https://github.com/wangfenjin"&gt;Wang Fenjin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Vega-Lite is a powerful library and there are many different visualizations you can create from your relational data using xeus-sql. You can see some other examples below:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Examples of different visualizations generated with xeus-sql and xvega-lite by Wang Fenjin" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/004-1_Rh5QEx6ntJHJ7gSpxgp29A.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;a href="https://github.com/wangfenjin/xeus-tidb/blob/develop/examples/TiDB.ipynb"&gt;Examples&lt;/a&gt; of different visualizations generated with xeus-sql and xvega-lite by Wang Fenjin&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We’re looking forward to exchange with users and learn more about their use cases for xeus-sql and xvega, so if you have any questions please drop them on the public &lt;a href="https://gitter.im/QuantStack/Lobby"&gt;QuantStack chat&lt;/a&gt;, or open an issue at the project’s &lt;a href="https://github.com/jupyter-xeus/xeus-sql"&gt;GitHub page&lt;/a&gt;. As always, contributions are welcome!&lt;/p&gt;
&lt;h3 id="try-it-online"&gt;Try it online!&lt;/h3&gt;
&lt;p&gt;You can try the stable version on Binder without the need of installing anything on your computer just by clicking on the image below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-sql/stable?urlpath=lab/tree/examples/XVega%20operations.ipynb"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/005-0_cZCb7p2dP8Y27P7P.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;We are grateful to &lt;a href="https://github.com/wangfenjin"&gt;Wang Fenjin&lt;/a&gt; for his contributions with respect to the use of vega specs in xeus-sql. The plotting mini-language was built upon the xvega library by &lt;a href="https://twitter.com/mad_tandon"&gt;Madhur Tandon&lt;/a&gt;. We are also grateful to the &lt;a href="https://github.com/SOCI/soci/"&gt;SOCI&lt;/a&gt; contributors for their awesome package that made this work possible.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/an-sql-solution-for-jupyter/images/006-1_S-w69baox7Q1D997a5yKNw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;My name is &lt;a href="https://twitter.com/mari_meir"&gt;Mariana Meireles&lt;/a&gt; and I’m a software developer working for &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;. I care deeply about the impacts that technology has in the world and try my best to be the change I want to see by contributing to open source projects that stand upon libre and diverse standards.&lt;/p&gt;
</content><category term="kernels"/></entry><entry><title>SlicerJupyter: a 3D Slicer kernel for interactive publications</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/" rel="alternate"/><published>2020-07-08T16:29:00+00:00</published><updated>2020-07-08T16:58:00+00:00</updated><author><name>Jean-Christophe Fillion-Robin</name></author><id>tag:jasongrout.github.io,2020-07-08:/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/</id><summary type="html">&lt;p&gt;Use Jupyter and 3D Slicer kernel to implement biomedical data processing workflows in a notebook&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The Jupyter ecosystem is a powerful platform for exploratory computational science, and now it can connect with some of the deep and rich domain-specific desktop applications that have decades of feature development already invested in them. With the integration of &lt;a href="https://www.slicer.org/"&gt;3D Slicer&lt;/a&gt; with Jupyter through the &lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;xeus-python&lt;/a&gt;’s interpreter, we demonstrate how a &lt;a href="https://www.qt.io/"&gt;Qt-based&lt;/a&gt; graphical desktop application with 3D visualization provided by &lt;a href="https://vtk.org/"&gt;Visualization Toolkit (VTK)&lt;/a&gt;, image processing provided by the &lt;a href="https://itk.org/"&gt;Insight Toolkit (ITK)&lt;/a&gt;, can be used through a Jupyter notebook. This approach is available on the &lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;SlicerJupyter&lt;/a&gt; GitHub repository and could be extended to other applications that embed Python, such as Blender, FreeCAD, or ParaView.&lt;/p&gt;
&lt;p&gt;This xeus-python integration is beneficial both for the Jupyter ecosystem and desktop applications. Features that have been developed for decades for desktop applications become readily available for Jupyter users without learning a new working environment or redeveloping features. For a desktop application, the Jupyter notebook can serve as a way to create reproducible data processing workflows, scientific publications, and maintainable tutorials without requiring local software installation. From Jupyter, you can now quickly create simple medical imaging applications with extremely rich interactivity from 3D Slicer. Read below for a discussion of the features available and the history of the project.&lt;/p&gt;
&lt;h2 id="powerful-medical-imaging-capabilities-available-through-jupyter"&gt;Powerful Medical Imaging Capabilities Available Through Jupyter&lt;/h2&gt;
&lt;p&gt;3D Slicer (or Slicer for short) is a C++ desktop application that uses Qt, ITK, and VTK libraries for visualization and medical image analysis. Slicer’s embedded Python interpreter makes all its features accessible with the Python programming language. Slicer has a simple built-in console to run Python commands interactively and can run Python scripts from files, but these are not as convenient as cell-base interactive notebooks, which have become popular among data scientists and researchers in recent years.&lt;/p&gt;
&lt;p&gt;By integrating the xeus-python kernel, we can use a Slicer process as a Jupyter kernel. xeus-python leverages the &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;xeus&lt;/a&gt; C++ implementation of the Jupyter kernel protocol. xeus-python is &lt;a href="/posts/2019/a-new-python-kernel-for-jupyter/"&gt;an alternative to ipykernel&lt;/a&gt;, which can be used with a vanilla CPython interpreter, interfacing to standard CPython Jupyter widgets like &lt;a href="https://github.com/InsightSoftwareConsortium/itkwidgets"&gt;itkwidgets&lt;/a&gt;, but it can also be coupled with custom interpreters and GUI event loops, like the Slicer interpreter and its Qt event loop. This allows you to represent a complete scene in Medical Reality Markup Language, MRML, Slicer’s internal data structure. The kernel exposes the full medical imaging API and representation of your data in a meaningful way for Python developers, allowing access through standard Python ecosystem formats such as pandas dataframes and NumPy arrays in the Notebook.&lt;/p&gt;
&lt;h3 id="interactivity-levels"&gt;Interactivity Levels&lt;/h3&gt;
&lt;p&gt;You can also use Jupyter interactive widgets (sliders, buttons, etc.) to control Slicer, modify data, or adjust processing and visualization parameters. Interactivity can be implemented at different levels.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Level 1&lt;/strong&gt;: Standard Jupyter widgets display application specific objects by automatic conversion of application-specific data objects to standard Python objects. For example, Slicer markup fiducial lists are displayed as a nicely formatted table and model nodes are rendered as 3D objects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level 2&lt;/strong&gt;: Static image widgets display content that the desktop application renders. These widgets can be made interactive by modifying data and rendering parameters using additional standard widgets. This makes rich visualization capabilities — sophisticated rendering various data types, rendering of very large data sets, etc. directly available in Jupyter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level 3&lt;/strong&gt;: Dynamic viewer widgets display 2D and 3D views rendered by the desktop application. Mouse and keyboard events are forwarded to the desktop application that allows zooming/rotating views, and to utilize all 3D interactions implemented in the desktop such as placing annotations, making measurements, or segmenting images the same way as if it was done on the desktop appication’s screen. This is implemented in the Slicer Jupyter kernel using &lt;a href="https://ipycanvas.readthedocs.io/en/latest/"&gt;ipycanvas&lt;/a&gt; and &lt;a href="https://github.com/mwcraig/ipyevents"&gt;ipyevents&lt;/a&gt; packages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Level 4&lt;/strong&gt;: Full desktop graphical user interface integration. Users can see parts of the application window rendered in notebook cells, including standard desktop widgets (sliders, menus, etc.). It is implemented using noVNC and TigerVNC in Slicer Jupyter. This is particularly useful when the application runs on a remote server.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/oZ3_cRXX2QM" title="Medical image processing in your web browser using Jupyter notebooks and 3D Slicer" 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;figcaption&gt;
&lt;p&gt;Video demonstrating how to run 3D Slicer using Binder&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These interactive tools allow developers to implement complete data processing workflows in a notebook, even if certain steps require manual user inputs such as segmenting 3D regions or setting seed points.&lt;/p&gt;
&lt;h3 id="try-it-online"&gt;Try it online!&lt;/h3&gt;
&lt;p&gt;Since Jupyter notebooks can be used from any web browser, it can essentially turn any desktop application to a web application. By setting up a remote Jupyter server, users do not have to install anything on their computers. We have set up a demonstration of this using Binder (&lt;a href="http://www.mybinder.org"&gt;www.mybinder.org&lt;/a&gt;) that anybody can try at &lt;a href="https://mybinder.org/v2/gh/Slicer/SlicerNotebooks/master"&gt;https://mybinder.org/v2/gh/Slicer/SlicerNotebooks/master&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://mybinder.org/v2/gh/Slicer/SlicerNotebooks/master"&gt;&lt;img alt="Click on the binder image to launch the demo" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/001-0_powLrWNrbtb0dRWs.webp" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;Click on the binder image to launch the demo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The docker image that installs and configures Slicer and all dependencies (ipycanvas, ipyevents, VNC, etc.) is available at &lt;a href="https://github.com/Slicer/SlicerDocker/tree/master/slicer-notebook"&gt;https://github.com/Slicer/SlicerDocker/tree/master/slicer-notebook&lt;/a&gt;. For deployment to non-technical users, applications can be deployed using Voilà (&lt;a href="https://voila.readthedocs.io/"&gt;https://voila.readthedocs.io/&lt;/a&gt;), which only shows relevant content and interactive widgets, so the notebook looks like a simple dynamic web page.&lt;/p&gt;
&lt;p&gt;The current implementation is already stable and offers a wide range of features, but there is still room for design and performance improvements. For example, we could not implement fully automatic conversion of application-specific data objects to displayable Python objects (due to complex implementation of display hooks); xeus-python debugger’s threading model needs to be improved to allow using it without locking the application’s main thread; and dynamic viewer widget’s performance (level 3 interaction) could be optimized to achieve higher refresh rates.&lt;/p&gt;
&lt;h2 id="history-of-slicer-and-this-integration"&gt;History of Slicer and this integration&lt;/h2&gt;
&lt;p&gt;Built over two decades with support from the NIH and a worldwide open source developer community, 3D Slicer is a unique, multi-platform desktop application for analysis, integration, and visualization of medical images that is heavily used by researchers globally for basic and applied research in a wide range of topics. The 3D Slicer Community includes physician-scientists with disease-specific knowledge of clinical challenges, computer scientists and physicists who develop novel algorithms, imaging informatics researchers, software engineers with the ability to understand clinical problems and create reliable tools, and application engineers with the multidisciplinary skills to deploy these tools in a range of cancer research settings. Slicer is maintained by Kitware, Inc., and the NAMIC consortium, and has an active open source software community. Slicer is used in hospitals and by researchers, with more than 10k academic citations and more than 150k downloads in the last year alone.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://download.slicer.org"&gt;&lt;img alt="Click on the image to download 3D Slicer" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/002-1_ePhHKkjqV-AW0mvDkaeurw.webp" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;Click on the image to download 3D Slicer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It can be used for image processing workflows on 2D, 3D, and 4D images. What makes Slicer so useful is its huge set of community contributed modules that extend its functionality, as well as its ability to read DICOM and a wide array of exotic file formats.&lt;/p&gt;
&lt;p&gt;Slicer has been scriptable in Python for well over a decade, but a robust &lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;SlicerJupyter&lt;/a&gt; extension is born from significant development effort by the community. The idea for this integration dates back to SciPy in 2014, when Jean-Christophe Fillion-Robin of Kitware organized a sprint intended to integrate Slicer and an IPython notebook, motivated by the dream of creating interactive and fun tutorials for Slicer. Mike Sarahan and Jean-Christophe created this proof of concept &lt;a href="https://github.com/commontk/QEmbedIPython#qt-embed-ipython"&gt;https://github.com/commontk/QEmbedIPython#qt-embed-ipython&lt;/a&gt;, but it was far from usable. In 2015 Matt McCormick of Kitware, created the &lt;a href="https://github.com/Slicer/SlicerDocker"&gt;SlicerDocker&lt;/a&gt; repository to support headless builds and rendering in a Docker image. Then in June 2018, while attending the Slicer Project Week, Andras Lasso (Queen’s University) and Jean-Christophe learned about Xeus, a C++ implementation of the Jupyter kernel protocol developed by QuantStack that would help streamline the integration of Slicer with Jupyter. To support this effort, Andras and Jean-Christophe created the &lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;Slicer/SlicerJupyter&lt;/a&gt; GitHub repository. They also contributed changes to Xeus to support this new integration paradigm where the event loop of the kernel is driven by a Qt-based Desktop application (see &lt;a href="https://github.com/jupyter-xeus/xeus/pull/63"&gt;here&lt;/a&gt;). Building on this foundation, Isaiah Norton, then working at Brigham and Women’s Hospital, contributed additional improvements like a better auto-completion using jedi (see &lt;a href="https://github.com/Slicer/SlicerJupyter/pull/12"&gt;https://github.com/Slicer/SlicerJupyter/pull/12&lt;/a&gt;) as well as integration with Binder. More recently, Jean-Christophe and Sylvain Corlay (QuantStack) met after the Slicer project week while attending SciPy in Austin. Following the creation of a new project called xeus-python (started by Martin Renou at QuantStack), we took the integration of Jupyter and Slicer to the next level by adding support for improved interactive use and MRML data node visualization directly in the notebook.&lt;/p&gt;
&lt;h2 id="about-the-authors"&gt;About the authors&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Alphabetically ordered&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sylvain Corlay&lt;/strong&gt; is the founder and CEO of QuantStack, and a core Jupyter developer. He co-authored xeus and xeus-python.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Sylvain Corlay" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/003-1_DKAhOe_Y4JdGYkgi9jHBSQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Sylvain Corlay&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Jean-Christophe Fillion-Robin&lt;/strong&gt; is an open-source enthusiast, original author of the SlicerJupyter extension and a principal engineer at Kitware Inc where he leads the development of “3D Slicer” based commercial applications. J-Christophe also maintains &lt;a href="https://scikit-build.org"&gt;scikit-build&lt;/a&gt;, an improved build system generator for CPython C/C++/Fortran/Cython extensions.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jean-Christophe Fillion-Robin" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/004-1_slg9clPxU3DhL4W2pE2kCA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jean-Christophe Fillion-Robin&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Mike Grauer&lt;/strong&gt; is a Technical Leader on the data and analytics team at Kitware. He is specialized in building scalable server-side processing frameworks that enable scientific workflows over web platforms.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Mike Grauer" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/005-1_SuBw-BD8efPla8tfd98RUg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Mike Grauer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Andras Lasso&lt;/strong&gt; is an original author of the SlicerJupyter extension, Senior Research Engineer and Associate Director of the Laboratory for Percutaneous Surgery at Queen’s University.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Andras Lasso" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/006-1_kINpFRIt94iHaxTA90zncQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Andras Lasso&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Matt McCormick&lt;/strong&gt; is an open source, medical imaging researcher working at Kitware Inc. Matt is an active, contributing member of scientific open source software efforts such as the Insight Toolkit (ITK) and scientific Python (SciPy) communities, and maintains the Jupyter 3D widget, &lt;a href="https://github.com/InsightSoftwareConsortium/itkwidgets"&gt;itkwidgets&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Matt McCormick" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/007-1_Pm17Os2v8Kf_GiZQw1G_ZQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Matt McCormick&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Isaiah Norton&lt;/strong&gt; is a Senior Software Developer at TileDB, Inc. with experience in digital pathology and image-guided surgical navigation.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Isaiah Norton" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/008-1_eR4Y9MX-2lcRsXEKodCJrQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Isaiah Norton&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Steve Pieper&lt;/strong&gt; is a Chief Architect and active developer of the 3D Slicer application for well over a decade. He is CEO of Isomics, Inc, where he uses a range of software technologies to perform medical imaging research with leading universities and companies.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Steve Pieper" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/009-1_aNwZz5TLENXuS2WQRb_N9w.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Steve Pieper&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Martin Renou&lt;/strong&gt; is a scientific software developer at QuantStack. He is the original author of xeus-python, the xeus-based Python kernel, and contributed to the new concurrency model used for the debugger.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Martin Renou" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/010-1_Cy9QfZytimqXQut2-bdcIA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Martin Renou&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Mike Sarahan&lt;/strong&gt; is a software engineer at RStudio, PBC working on bringing language ecosystems together. He is passionate about making software work for data scientists in ways that are easy to maintain and improve.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Mike Sarahan" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/slicerjupyter-a-3d-slicer-kernel-for-interactive/images/011-1_YKNdPIBH39iFBIL9nD_kfg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Mike Sarahan&lt;/figcaption&gt;
&lt;/figure&gt;
</content><category term="kernels"/><category term="science"/><category term="visualization"/></entry><entry><title>A Jupyter kernel for SQLite</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/" rel="alternate"/><published>2020-06-11T08:42:00+00:00</published><updated>2020-12-30T15:29:00+00:00</updated><author><name>Mariana Meireles</name></author><id>tag:jasongrout.github.io,2020-06-11:/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/</id><summary type="html">&lt;p&gt;While it is well known in the Python scientific computing community, Jupyter is in fact a language-agnostic development environment…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;While it is well known in the Python scientific computing community, Jupyter is in fact a language-agnostic development environment. High-quality language kernels exist for the main languages of data sciences, such as &lt;a href="https://github.com/jupyter-xeus/xeus-python"&gt;Python&lt;/a&gt;, &lt;a href="https://github.com/jupyter-xeus/xeus-cling"&gt;C++&lt;/a&gt;, &lt;a href="https://github.com/JuniperKernel/JuniperKernel"&gt;R&lt;/a&gt; or &lt;a href="https://github.com/JuliaLang/IJulia.jl"&gt;Julia&lt;/a&gt;.&lt;br&gt;
But another important tool for data science is the SQL family of programming languages. Today, we announce the release of a Jupyter kernel for SQLite.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/001-1_pzPTGCJVgIO10N7OwOGWKw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;This new kernel allows the user to use the complete SQLite syntax as well as some extra operations such as opening or closing a database file, or visualizing the data in different ways using Jupyter magics.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="HTML and Text table visualizations for different Jupyter interfaces" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/002-1_Pj5UkLChFO3BIMx7NIDRdA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;HTML and Text table visualizations for different Jupyter interfaces&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To make the task of implementing a new Kernel for Jupyter easier, we make use of &lt;a href="https://github.com/jupyter-xeus/xeus"&gt;&lt;strong&gt;Xeus&lt;/strong&gt;&lt;/a&gt;, a library providing a solid implementation of the Jupyter kernel protocol, so that we can focus on implementing the language-specific parts of the kernel.&lt;/p&gt;
&lt;p&gt;As a connection between SQLite’s native C implementation and C++ we’re using SRombauts’ library &lt;a href="https://github.com/SRombauts/SQLiteCpp"&gt;&lt;strong&gt;SQLiteCpp&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="current-state"&gt;Current state&lt;/h2&gt;
&lt;p&gt;xeus-SQLite is still under active development but it offers a fully functional SQLite interface and magics to perform higher-level operations that are outside of the scope of the SQL syntax, such as creating, opening, or closing SQLite databases. Some of the things you can do with xeus-SQLite are creating a new database, loading it, backing it up or deleting it. You can also check if a table exists, set and reset keys of a database and get information about it. You can find more about the use of these methods in SQLite’s &lt;a href="https://xeus-sqlite.readthedocs.io/en/latest/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;xeus-SQLite provides rich HTML display of tables in the Jupyter Notebook and Jupyter Lab. For the Jupyter Console we make use of the &lt;a href="https://github.com/p-ranav/tabulate"&gt;&lt;strong&gt;tabulate&lt;/strong&gt;&lt;/a&gt; library for textual display.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Different commands being executed on xeus-SQLite" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/003-1_BIDimBvS_g8QH5fuHS_QRQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Different commands being executed on xeus-SQLite&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="the-future"&gt;The future&lt;/h2&gt;
&lt;p&gt;For the future of xeus-SQLite we want to create an intuitive form of visualizing data: creating plots, graphs, charts, maps and much more from your SQLite query results, all in the same notebook. Currently we’re exploring the use of &lt;a href="https://vega.github.io/vega/"&gt;&lt;strong&gt;Vega&lt;/strong&gt;&lt;/a&gt; in xeus-SQLite, a declarative language for creating interactive visualization designs and can do bar plots using jupyter magics:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="SQLite query result with bar and table outputs for same query" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/004-1_eWZyoVvu5c_k_Yerv1eT-A.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;SQLite query result with bar and table outputs for same query&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This feature is still in very early stages and being developed in this &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite/tree/vega-visualization"&gt;branch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We also plan on producing a static build of xeus-SQLite bundling xeus and the SQLite library into a single executable that can be easily distributed.&lt;/p&gt;
&lt;h3 id="try-it-online"&gt;Try it online!&lt;/h3&gt;
&lt;p&gt;You can try the stable version without the need of installing anything on your computer just by clicking on the image below:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mybinder.org/v2/gh/jupyter-xeus/xeus-sqlite/stable"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/005-1_wiJsOB9OWiAGwuaoycfc0w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This won’t grant you access to the experimental branch where we’re testing, if you’re interested in trying it please check the &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite#installation"&gt;installation&lt;/a&gt; guide and make sure you’re in the right &lt;a href="https://github.com/jupyter-xeus/xeus-sqlite/tree/vega-visualization"&gt;branch&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="installation"&gt;Installation&lt;/h3&gt;
&lt;p&gt;Note that you first need to have Jupyter installed on your computer. You can install &lt;code&gt;xeus-sqlite&lt;/code&gt; using mamba:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mamba install xeus-sqlite -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install xeus-sqlite -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="about-the-author"&gt;About the author&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-jupyter-kernel-for-sqlite/images/006-1_S-w69baox7Q1D997a5yKNw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;My name is &lt;a href="https://twitter.com/mari_meir"&gt;Mariana Meireles&lt;/a&gt; and I’m a software developer working for &lt;a href="http://quantstack.net/"&gt;QuantStack&lt;/a&gt;. I care deeply about the impacts that technology has in the world and try my best to be the change I want to see by contributing to open source projects that stand upon libre and diverse standards.&lt;/p&gt;
&lt;p&gt;Prior to QuantStack I worked as a developer on the PySide team at the Qt Company and as a web performance developer at Mozilla.&lt;/p&gt;
</content><category term="kernels"/></entry><entry><title>A visual debugger for Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/" rel="alternate"/><published>2020-03-25T15:53:00+00:00</published><updated>2020-03-26T09:12:00+00:00</updated><author><name>Project Jupyter</name></author><id>tag:jasongrout.github.io,2020-03-25:/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/</id><summary type="html">&lt;p&gt;Most of the progress made in software projects comes from incrementalism. The ability to quickly see the outcome of an execution and…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Most of the progress made in software projects comes from &lt;em&gt;incrementalism&lt;/em&gt;. The ability to quickly see the outcome of an execution and iterate has been one of the main reasons for the success of Jupyter, especially in scientific exploratory workflows.&lt;/p&gt;
&lt;p&gt;Jupyter users like to &lt;em&gt;experiment&lt;/em&gt; in the notebook, and to use the notebook as an interactive communication tool. However, for more classical software development tasks such as the refactoring of a large codebase, they often switch to general-purpose IDEs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLab environment." src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/001-1_u8y-ggU2O513KjXdGbdEuQ.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLab environment.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Jupyter project has made strides in the past few years towards filling that gap, notably with the &lt;strong&gt;JupyterLab&lt;/strong&gt; project, which enables a richer UI including a file browser, text editors, consoles, notebooks, and a rich layout system.&lt;/p&gt;
&lt;p&gt;However, a missing piece (which has remained one of the main reasons for users to switch to a different tool) is a &lt;strong&gt;visual debugger&lt;/strong&gt;. This feature has long been requested by users, especially those accustomed to general-purpose development environments.&lt;/p&gt;
&lt;h2 id="a-debugger-for-jupyter"&gt;A debugger for Jupyter&lt;/h2&gt;
&lt;p&gt;Today, after several months of development, we are glad to announce the first public release of the Jupyter visual debugger!&lt;/p&gt;
&lt;p&gt;This is just the first release, but we can already set breakpoints in notebook cells and source files, inspect variables, navigate the call stack and more.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screencast of the JupyterLab visual debugger in action" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/002-1_NP0bYBdrhwgpJpKDhPLWrQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Screencast of the JupyterLab visual debugger in action&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="try-the-debugger-on-binder"&gt;Try the debugger on binder&lt;/h2&gt;
&lt;p&gt;You can also try the debugger online with binder. Just click on the binder link:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://mybinder.org/v2/gh/jupyterlab/debugger/stable?urlpath=/lab/tree/examples/index.ipynb"&gt;&lt;img alt="Click on the binder link to launch the demo" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/003-1_NbZ_56IL0J-q0V32qgiq3A.webp" loading="lazy" data-body-image=""&gt;&lt;/a&gt;
&lt;figcaption&gt;Click on the binder link to launch the demo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The debugger &lt;em&gt;&lt;strong&gt;front-end&lt;/strong&gt;&lt;/em&gt; can be installed as a JupyterLab extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;labextension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;@jupyterlab&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;debugger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;The debugger front-end will be included in JupyterLab by default in a future release.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the &lt;em&gt;&lt;strong&gt;back-end&lt;/strong&gt;&lt;/em&gt;, a kernel implementing the &lt;strong&gt;Jupyter Debug Protocol&lt;/strong&gt; (which will be detailed in the next section) is required. The only kernel implementing this protocol, for now, is &lt;code&gt;xeus-python&lt;/code&gt; a new Jupyter kernel for the Python programming language. (Support for the debugger protocol in ipykernel is also on the roadmap).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install xeus-python -c conda-forge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once xeus-python and the debugger extension are installed, you should be all set to use the Jupyter visual debugger!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Depending on the platform, PyPI wheels are available for xeus-python, but they are still &lt;em&gt;&lt;strong&gt;experimental&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="the-jupyter-debug-protocol"&gt;The Jupyter Debug Protocol&lt;/h2&gt;
&lt;h3 id="new-message-types-for-the-control-and-iopub-channels"&gt;New message types for the Control and IOPub channels&lt;/h3&gt;
&lt;p&gt;Jupyter kernels (the part of the infrastructure that executes the user’s code) communicate with the rest of the infrastructure with a &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;well-specified inter-process communication protocol&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Several communication channels exist, such as&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;Shell&lt;/strong&gt; channel, which is a request/reply channel for e.g. execution requests&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;IOPub&lt;/strong&gt; channel, which is a one-directional communication channel from the kernel to the client, and is used e.g. to forward the content of the standard output streams (stdout and stderr).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;Control&lt;/strong&gt; channel is similar to Shell but operates on a separate socket so that &lt;em&gt;&lt;strong&gt;messages are not queued behind execution requests&lt;/strong&gt;&lt;/em&gt;, and have a higher priority. Control was already used for Interrupt and Shutdown requests, and we decided to use the same channel for the commands sent to the debugger.&lt;/p&gt;
&lt;p&gt;Two message types were added to the protocol:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;debug_[request/reply]&lt;/code&gt; to request specific actions to be performed by the debugger such as adding a breakpoint or stepping into a code, which is sent to the &lt;strong&gt;Control&lt;/strong&gt; channel.&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;debug_event&lt;/code&gt; uni-directional message used by debugging kernels to send debugging events to the front-end. Debug events are sent over the IOPub channel.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="extending-the-debug-adapter-protocol"&gt;Extending the Debug Adapter Protocol&lt;/h3&gt;
&lt;p&gt;A key principle to the Jupyter design is the &lt;strong&gt;agnosticism to the programming language&lt;/strong&gt;. It is important for the Jupyter debug protocol to be adaptable to other kernel implementations.&lt;/p&gt;
&lt;p&gt;A popular standard for debugging is Microsoft’s “&lt;strong&gt;Debug Adapter Protocol&lt;/strong&gt;” (DAP) which is a JSON-based protocol underlying the debugger of Visual Studio Code and for which there already exist multiple language back-ends.&lt;/p&gt;
&lt;p&gt;It was therefore natural for us to use the DAP messages over the &lt;code&gt;debug_[request/reply]&lt;/code&gt; and &lt;code&gt;debug_event&lt;/code&gt; messages that we just added.&lt;/p&gt;
&lt;p&gt;However, it was not quite &lt;em&gt;sufficient&lt;/em&gt; in the case of Jupyter. Indeed&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In order to support page reloading, or a client connecting at a later stage, Jupyter kernels must store the state of the debugger (breakpoints, whether the debugger is currently stopped). The front-end can request that state over with a &lt;code&gt;debug_request&lt;/code&gt; message.&lt;/li&gt;
&lt;li&gt;In order to support the debugging of notebook cells and of Jupyter consoles, which are not based on source files, we also needed messages to submit code to the debugger to which breakpoints can be added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Besides these two differences, the content of the debug requests and replies corresponds to the debug adapter protocol.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All these extensions to the Jupyter kernel protocol have been proposed for inclusion in the official specification. The JEP (Jupyter Enhancement Proposal) can be found &lt;a href="https://github.com/jupyter/enhancement-proposals/pull/47"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="xeus-python-the-first-jupyter-kernel-to-support-debugging"&gt;Xeus-python, the first Jupyter Kernel to support debugging&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is a C++ implementation of the Jupyter kernel protocol. It is not a kernel by itself but a library that helps kernel authoring. &lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is useful when developing a kernel for a language that has a C or a C++API (like Python, Lua, or SQL). It takes the cumbersome task of implementing the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;Jupyter messaging protocol&lt;/a&gt; for the kernel author to focus on the core interpreter tasks: executing code, inspecting, etc.&lt;/p&gt;
&lt;p&gt;Several kernels have been developed with xeus, including the popular &lt;a href="https://github.com/jupyter-xeus/xeus-cling"&gt;xeus-cling&lt;/a&gt; kernel for the C++ programming language, based on the cling C++ interpreter from CERN. The &lt;a href="https://github.com/jupyter-xeus/xeus-python.git"&gt;xeus-python&lt;/a&gt; kernel is an alternative Python kernel to ipykernel, based on xeus. The first release of the xeus-python kernel was announced on this blog earlier this year: &lt;a href="/posts/2019/a-new-python-kernel-for-jupyter/"&gt;https://blog.jupyter.org/a-new-python-kernel-for-jupyter-fcdf211e30a8&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Xeus-python was an appropriate choice for this first implementation of the debugging protocol because&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it has a &lt;strong&gt;pluggable concurrency model&lt;/strong&gt;, which allowed running the processing of the Control channel in a different thread.&lt;/li&gt;
&lt;li&gt;it has a &lt;strong&gt;lighter-weight codebase&lt;/strong&gt; which made it a convenient sandbox to iterate upon. Implementing the first version of the protocol in ipykernel would have required more significant refactoring and consensus building at an early stage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-roadmap-of-xeus-python"&gt;The roadmap of Xeus-python&lt;/h3&gt;
&lt;p&gt;The short-term roadmap for xeus-python includes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;adding support for IPython magics in xeus-python, which is the main missing feature with respect to ipykernel.&lt;/li&gt;
&lt;li&gt;improving the PyPI wheels of xeus-python.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="what-about-other-kernels"&gt;What about other kernels?&lt;/h3&gt;
&lt;p&gt;The work in the front-end is valid for any kernel implementing the extended kernel protocol.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We will be working in 2020 to enable debugging with as many kernels as possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This will soon be the case for other xeus-based kernels which share a large part of the implementation with xeus-python, such as xeus-cling.&lt;/p&gt;
&lt;h2 id="diving-into-the-debugger-front-end-architecture"&gt;Diving into the debugger front-end architecture&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/jupyterlab/debugger"&gt;debugger extension for JupyterLab&lt;/a&gt; provides what users would typically expect from an IDE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a sidebar with a variable explorer, a list of breakpoints, a source preview and the possibility to navigate the call stack&lt;/li&gt;
&lt;li&gt;the ability to set breakpoints directly next to the code, namely in code cells and code consoles&lt;/li&gt;
&lt;li&gt;visual markers to indicate where the current execution has stopped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When working with Jupyter notebooks, the state of the execution is kept in the kernel. But a cell can be executed and then deleted from the notebook. What should happen when a user wants to step in deleted code?&lt;/p&gt;
&lt;p&gt;The extension supports that particular use case and enables retrieving a read-only view of the previously executed cell.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Stepping into a deleted cell" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/004-0_gsIleEb7Q-sq05f0.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Stepping into a deleted cell&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Consoles and files also have support for debugging.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Debugging code consoles in JupyterLab" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/005-0_G5-dLqkhH5CEvQQv.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Debugging code consoles in JupyterLab&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Debugging files in JupyterLab" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/006-0_ZejsDIpKOEPDgoTs.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Debugging files in JupyterLab&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Debugging can be enabled on a notebook level, which lets users debug a notebook and work on a different one at the same time.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Debugging multiple notebooks simultaneously" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/007-0_EtApkSznZEY-GJhA.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Debugging multiple notebooks simultaneously&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Variables can be inspected using a tree viewer and a table viewer:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The variable explorer" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/008-0_9pVDg58cTz5Yezw8.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The variable explorer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The debugger extension for JupyterLab has been designed to work with any kernel that supports debugging.&lt;/p&gt;
&lt;p&gt;By relying on the Debug Adapter Protocol, the debugger extension abstracts away language-specific features and provides a consistent debugging interface to the user.&lt;/p&gt;
&lt;p&gt;The following diagram shows how the debug messages flow between the user, the JupyterLab extension and the kernel during a debugging session.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Using the Debug Adapter Protocol in the debugger extension (source)" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/009-0_fwLQQWxKtGUiMpq1.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Using the Debug Adapter Protocol in the debugger extension (&lt;a href="https://github.com/jupyterlab/debugger/issues/64"&gt;source&lt;/a&gt;)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="future-developments"&gt;Future developments&lt;/h2&gt;
&lt;p&gt;In 2020, we plan on making major improvements to the debugger experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for rich mime type rendering in the variable explorer.&lt;/li&gt;
&lt;li&gt;Support for conditional breakpoints in the UI.&lt;/li&gt;
&lt;li&gt;General improvements of the debugger user experience.&lt;/li&gt;
&lt;li&gt;Enable the debugging of &lt;a href="https://github.com/voila-dashboards/voila/"&gt;Voilà dashboards&lt;/a&gt;, from the JupyterLab Voilà preview extension.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgements"&gt;Acknowledgements&lt;/h2&gt;
&lt;p&gt;The JupyterLab debugger is the result of the collaboration and coordination of developers from several institutions, including QuantStack, Two Sigma, and Bloomberg.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The work on both the front-end and the back-end at &lt;a href="https://twitter.com/QuantStack"&gt;&lt;strong&gt;QuantStack&lt;/strong&gt;&lt;/a&gt; by &lt;a href="https://twitter.com/jtpio"&gt;&lt;strong&gt;Jeremy Tuloup&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://twitter.com/johanmabille"&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://twitter.com/martinRenou"&gt;&lt;strong&gt;Martin Renou&lt;/strong&gt;&lt;/a&gt;, and &lt;a href="https://twitter.com/SylvainCorlay"&gt;&lt;strong&gt;Sylvain Corlay&lt;/strong&gt;&lt;/a&gt;, was funded by &lt;a href="https://twitter.com/techatbloomberg"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The work on the Jupyter debugger by &lt;strong&gt;Borys Palka&lt;/strong&gt; and &lt;a href="https://twitter.com/micronova"&gt;&lt;strong&gt;Afshin Darian&lt;/strong&gt;&lt;/a&gt; was made possible by &lt;a href="https://twitter.com/twosigma"&gt;&lt;strong&gt;Two Sigma&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="about-the-developers"&gt;About the developers&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="Jeremy Tuloup" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/010-1_4RE97odgcRtRruh0najsVg.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jeremy Tuloup&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Jeremy Tuloup&lt;/strong&gt; is a Scientific Software developer at QuantStack. He authored a large part of the front-end of the JupyterLab debugger.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Borys Palka" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/011-1_IJw8VB7Sq8v4FwUgP7DY_Q.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Borys Palka&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Borys Palka&lt;/strong&gt; is a software developer at Codete. He authored a large part of the front-end of the JupyterLab debugger.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Johan Mabille" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/012-1_y5KkUTTgNArZGajQWW38EA.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Johan Mabille&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Johan Mabille&lt;/strong&gt; is a scientific software developer at QuantStack. Johan is a co-author of xeus, and developed the debugger extension to xeus-python. He also authored a large part of the debugger front-end.&lt;br&gt;
.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Martin Renou" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/013-1_GafIA37eUPz6XQDTKS6D8A.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Martin Renou&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Martin Renou&lt;/strong&gt; is a scientific software developer at QuantStack. He is the original author of xeus-python, the xeus-based Python kernel, and contributed to the new concurrency model used for the debugger.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Afshin Darian" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/014-1_FEspm1Hcty29Jxwpb-cHXw.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Afshin Darian&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Afshin Darian&lt;/strong&gt; is a software developer at Two Sigma, and one of the authors of JupyterLab.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;br&gt;
.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Sylvain Corlay" src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/015-1_aSY0NNigA16fvOHVZCOaeQ.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Sylvain Corlay&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Sylvain Corlay&lt;/strong&gt; is the founder and CEO of QuantStack, and a core Jupyter developer. He co-authored xeus and xeus-python.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2020/a-visual-debugger-for-jupyter/images/016-1_Trh8fKqP0c_4Vvnzmu5SvA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="JupyterLab"/><category term="kernels"/></entry><entry><title>Field Report on the Kernel Community Workshop</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/" rel="alternate"/><published>2019-10-07T13:24:00+00:00</published><updated>2019-10-16T14:53:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2019-10-07:/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/</id><summary type="html">&lt;p&gt;From May 27th to May 29th, thirty developers from the Jupyter community met in Paris for a three-days workshop on the Jupyter kernel…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From May 27th to May 29th 2019, thirty developers from the Jupyter community met in Paris for a three-days workshop on the Jupyter kernel protocol.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Attendees to the Jupyter Community Workshop on Kernels" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/images/001-1_AKYqXS6qtE0k6EcTKl3BEQ.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Attendees to the Jupyter Community Workshop on Kernels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For three days, attendees worked full time on the Jupyter project, including hacking sessions and discussions on improvements to Jupyter protocols and standards.&lt;/p&gt;
&lt;p&gt;We were lucky to count five core developers to the project and several more regular contributors to the larger ecosystem in the group!&lt;/p&gt;
&lt;p&gt;Beyond the hacking sessions, each day was concluded with a series of presentations and demos of the progress made during the workshop.&lt;/p&gt;
&lt;h2 id="why-a-workshop-on-jupyter-kernels"&gt;Why a workshop on Jupyter kernels?&lt;/h2&gt;
&lt;p&gt;The Jupyter Kernel protocol is one of the main extension points of the Jupyter ecosystem. Dozen of language kernels have been developed by the community, and these languages can then leverage the other components of the stack, such as the notebook, the console, and interactive widgets.&lt;/p&gt;
&lt;p&gt;One of the objectives of this event was to foster collaboration between kernel developers and core contributors and develop common tools used across all language kernels. Another goal was to work improving the protocol, to support visual debugging, parameterized kernels, etc.&lt;/p&gt;
&lt;h2 id="technical-achievements"&gt;Technical Achievements&lt;/h2&gt;
&lt;p&gt;A lot of work was done during the workshop. Several people were working on new Jupyter kernels for programming languages.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We were amazed to see how much was achieved in just three days. Today, we are still working on projects that stemmed from this community workshop.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="jupyterlab-debugger"&gt;JupyterLab Debugger&lt;/h3&gt;
&lt;p&gt;One area in which the Jupyter team is actively working is support for &lt;strong&gt;visual debugging in JupyterLab&lt;/strong&gt;. This is a major endeavor spanning from the front-end to the back-end, including changes to the Jupyter protocol.&lt;/p&gt;
&lt;p&gt;The kernel workshop was the occasion for several developers (&lt;a href="https://twitter.com/wuoulf"&gt;Wolf Vollprecht&lt;/a&gt;, &lt;a href="https://twitter.com/maartenbreddels"&gt;Maarten Breddels&lt;/a&gt;, &lt;a href="https://twitter.com/johanmabille"&gt;Johan Mabille&lt;/a&gt;, &lt;a href="https://twitter.com/lgouarin"&gt;Loic Gouarin&lt;/a&gt;) to get together and hack on the implementation of the frontend. A lot of work had been done ahead of the workshop with &lt;a href="https://github.com/QuantStack/xeus-python"&gt;a new Python kernel&lt;/a&gt; based on the &lt;a href="https://github.com/QuantStack/xeus/"&gt;Xeus&lt;/a&gt; library. This new kernel includes a backend to the &lt;strong&gt;Debug Adapter Protocol&lt;/strong&gt; over kernel messages.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The JupyterLab Visual Debugger" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/images/002-1_O0nYHhfEne2BH4XsknaPhg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The JupyterLab Visual Debugger&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;There are other key contributors to the debugger project who did not attend the community workshop. We should mention &lt;a href="https://twitter.com/jtpio"&gt;Jeremy Tuloup&lt;/a&gt; and &lt;a href="https://twitter.com/micronova"&gt;Afshin Darian&lt;/a&gt; who are spearheading the frontend development.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="a-calculator-jupyter-kernel-based-on-xeus"&gt;A Calculator Jupyter Kernel based on xeus&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://twitter.com/VasavanT"&gt;Vasavan Thirusittampalam&lt;/a&gt; and &lt;a href="https://twitter.com/ThLacharme"&gt;Thibault Lacharme&lt;/a&gt; were in the middle of their summer internships as scientific software developers at QuantStack when they attended the workshop. During the event, they set themselves up to implement a new Jupyter kernel in C++!&lt;/p&gt;
&lt;p&gt;At the end of the workshop, they were able to demonstrate a functional &lt;strong&gt;calculator kernel&lt;/strong&gt; based on &lt;a href="https://github.com/QuantStack/xeus/"&gt;xeus&lt;/a&gt;, a C++ implementation of the Jupyter protocol! This was later polished and resulted in the publication of a blog post on the Jupyter blog: “&lt;a href="/posts/2019/building-a-calculator-jupyter-kernel/"&gt;&lt;em&gt;&lt;strong&gt;Building a Calculator Jupyter Kernel&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;”. This example may serve as an example for people interested in creating new language kernels with xeus.&lt;/p&gt;
&lt;h3 id="a-prototype-julia-backend-to-jupyter-interactive-widgets"&gt;&lt;strong&gt;A prototype Julia backend to Jupyter interactive Widgets.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;During the workshop &lt;a href="https://twitter.com/SebasGuts"&gt;Sebastian Gutsche&lt;/a&gt; (who is the author of the &lt;a href="https://github.com/sebasguts/jupyter_kernel_singular"&gt;Singular&lt;/a&gt; Jupyter kernel and a co-author of the &lt;a href="https://github.com/gap-packages/JupyterKernel"&gt;GAP&lt;/a&gt; kernel), set himself to develop a backend to Jupyter interactive widgets for the &lt;strong&gt;Julia&lt;/strong&gt; progamming language. The current state of the code can be found &lt;a href="https://github.com/sebasguts/iwidgets"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Julia backend to jupyter widgets is still early-stage but should this project be completed, it would enable other Jupyter widget libraries for the users of the Julia Jupyter kernel, such as ipyvolume, ipyleaflet, bqplot, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
&lt;img alt="The Julia backend to Jupyter widgets" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/images/003-1_xv77xrNuUHKLjLinum0dlQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Julia backend to Jupyter widgets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="parameterized-kernelspecs"&gt;Parameterized Kernelspecs&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://twitter.com/ivanov"&gt;Paul Ivanov&lt;/a&gt; and &lt;a href="https://twitter.com/rgbkrk"&gt;Kyle Kelley&lt;/a&gt; worked on ironing out the proposal for the support of &lt;strong&gt;parameterized kernelspecs&lt;/strong&gt;. The objective is to make it possible to pass user-specified arguments to kernels upon launch.&lt;/p&gt;
&lt;p&gt;In the proposal, the parameters expected by the kernel executable and the possible values for these parameters may be specified in the kernelspec file or a companion file to the kernelspec in the form of a JSON schema.&lt;/p&gt;
&lt;p&gt;Paul and Kyle produced extensive notes on various aspects on the subjects such as&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the storing of previously used values of the parameters (in notebooks)&lt;/li&gt;
&lt;li&gt;the web UI design on how to specify parameters&lt;/li&gt;
&lt;li&gt;the definition of default values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More details will be shared at a later stage on the subject of parameterized kernelspecs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: the subject of parameterized kernelspecs was also central in the earlier community workshop about the Jupyter server organized at IBM by Luciano Resende.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="forking-of-jupyter-kernels"&gt;Forking of Jupyter Kernels&lt;/h3&gt;
&lt;p&gt;Starting of kernels is an expensive operation, and executing a whole notebook even more. For certain use cases (such as dashboards based on &lt;a href="https://github.com/voila-dashboards/voila"&gt;Voilà&lt;/a&gt;), it is useful to have a pre-executed notebook ready, so that dashboards can be presented instantly to a user. To enable this, &lt;a href="https://twitter.com/maartenbreddels"&gt;Maarten Breddels&lt;/a&gt; came up with the idea of forking kernels and put together a proof-of-concept implementation. The implementation can be found in pull requests to &lt;code&gt;jupyter_client&lt;/code&gt; (&lt;a href="https://github.com/jupyter/jupyter_client/pull/441"&gt;PR #441&lt;/a&gt;) and &lt;code&gt;ipykernel&lt;/code&gt; (&lt;a href="https://github.com/ipython/ipykernel/pull/410"&gt;PR #410&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Forking kernels will also allow interesting features such as an undo/rollback of cell execution in the notebook. While it is still a proof of concept, this subject has already sparked interest in the community.&lt;/p&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;This event would not have been possible without the generous support provided by &lt;a href="https://www.techatbloomberg.com/"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;, who made this workshop series possible&lt;/p&gt;
&lt;p&gt;We are grateful to &lt;a href="https://www.cfm.fr/"&gt;&lt;strong&gt;CFM&lt;/strong&gt;&lt;/a&gt; for gracefully hosting the event.&lt;/p&gt;
&lt;p&gt;We should also thank &lt;a href="https://twitter.com/ruv7?lang=en"&gt;&lt;strong&gt;Ana Ruvalcaba&lt;/strong&gt;&lt;/a&gt; from Project Jupyter for her incredible work on the logistics and finances of the Jupyter Community Workshop series.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/field-report-on-the-kernel-community-workshop/images/004-1_ZWbPC-X8_5rIU_kP03ztog.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="C++"/><category term="events"/><category term="kernels"/><category term="workshops"/></entry><entry><title>Building a Calculator Jupyter Kernel</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/" rel="alternate"/><published>2019-07-16T08:36:00+00:00</published><updated>2019-07-16T09:14:00+00:00</updated><author><name>QuantStack</name></author><id>tag:jasongrout.github.io,2019-07-16:/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/</id><summary type="html">&lt;p&gt;A step-by-step guide for authoring language kernels with Xeus&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;A step-by-step guide for authoring language kernels with Xeus&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="An early device for interactive computing" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/images/001-0_h6rfGRuTgOkwonN9.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;An early device for interactive computing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In order to provide a language-agnostic scientific development environment, the &lt;strong&gt;Jupyter project&lt;/strong&gt; is built upon a &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;&lt;em&gt;well-specified protocol&lt;/em&gt;&lt;/a&gt; to communicate with the &lt;em&gt;Kernel&lt;/em&gt;, the part of the infrastructure responsible for executing the code.&lt;/p&gt;
&lt;p&gt;For a programming language to leverage the potential of the Jupyter ecosystem, such as &lt;em&gt;JupyterHub&lt;/em&gt;, &lt;em&gt;JupyterLab&lt;/em&gt;, and &lt;em&gt;interactive widgets&lt;/em&gt;, all that is needed is a &lt;em&gt;Kernel&lt;/em&gt; to be created for that language that is, an executable implementing the specified inter-process communication. Dozens of kernels have already been implemented bringing Jupyter to many programming languages.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We are completing our engineering degree and interning at QuantStack. We recently attended the&lt;/em&gt; &lt;a href="/posts/2019/jupyter-community-workshop-building-upon-the-jupyter/"&gt;&lt;em&gt;Jupyter Community Workshop&lt;/em&gt;&lt;/a&gt; &lt;em&gt;on the kernel protocol that took place in Paris in late May. In this occasion, we set ourselves to write a new Jupyter kernel.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Today, we are proud to announce the first release of &lt;a href="https://github.com/QuantStack/xeus-calc"&gt;&lt;em&gt;&lt;strong&gt;xeus-calc&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;, a calculator kernel for Jupyter! xeus-calc is meant to serve as a &lt;em&gt;minimal,&lt;/em&gt; &lt;em&gt;self-contained&lt;/em&gt;example of Jupyter kernel. It is built upon the &lt;a href="https://github.com/QuantStack/xeus/"&gt;&lt;em&gt;&lt;strong&gt;xeus&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; project, a modern C++ implementation of the protocol. This article is a step-by-step description on how the kernel was implemented.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You may find this post especially useful if you are creating a new programming language and you want it to work in Jupyter from the start.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="xeus"&gt;Xeus&lt;/h2&gt;
&lt;p&gt;Implementing the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;&lt;strong&gt;Jupyter kernel protocol&lt;/strong&gt;&lt;/a&gt; from scratch may be a tedious and difficult task. One needs to deal with ZMQ sockets and complex concurrency issues, rely on third-party libraries for cryptographically signing messages or parsing JSON efficiently. This is where the &lt;a href="https://github.com/QuantStack/xeus/"&gt;&lt;em&gt;&lt;strong&gt;xeus&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; project comes into play: it takes all of that burden so that developers can focus on the parts that are specific to their use case.&lt;/p&gt;
&lt;p&gt;In the end, the kernel author only needs to implement a small number of virtual functions inherited from the &lt;code&gt;xinterpreter&lt;/code&gt; class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;xeus/xinterpreter.hpp&amp;quot;&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;quot;nlohmann/json.hpp&amp;quot;&lt;/span&gt;
&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;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;xeus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;xinterpreter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nl&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="nt"&gt;nlohmann&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&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;namespace custom
{
    class custom_interpreter : public xinterpreter
    {
    public:
&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;        custom_interpreter() = default;
        virtual ~custom_interpreter() = default;
&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;     private:
&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;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;nl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execute_request_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execution_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="nx"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;code&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="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;silent&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="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;store_history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="nx"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;node_type&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user_expressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;allow_stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&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;nl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;complete_request_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cursor_pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&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;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inspect_request_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cursor_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;detail_level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&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;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is_complete_request_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&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;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;nl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kernel_info_request_impl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&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;Typically, a kernel author will make use of the C or C++ API of the target programming language and embed the interpreter into the application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This differs from the &lt;em&gt;wrapper kernel&lt;/em&gt; approach documented in the ipykernel package where kernel authors make use of the kernel protocol implementation of ipykernel, typically spawning a separate process for the interpreter and capturing its standard output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jupyter kernels based on xeus include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus-cling"&gt;&lt;em&gt;&lt;strong&gt;xeus-cling&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;: a C++ kernel built upon the cling C++ interpreter from CERN&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus-python"&gt;&lt;strong&gt;xeus-python&lt;/strong&gt;&lt;/a&gt;: a new Python kernel for Jupyter.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/JuniperKernel/JuniperKernel"&gt;&lt;em&gt;&lt;strong&gt;JuniperKernel&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;: a new R kernel for Jupyter based on xeus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, instead of calling into the API of an external interpreter, we implement the internal logic of the calculator in the kernel itself.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Exposing the xeus calculator to Jupyter" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/images/002-0_Q2jOwO75XQvNuqaR.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Exposing the xeus calculator to Jupyter&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="a-calculator-project"&gt;A calculator project&lt;/h3&gt;
&lt;p&gt;First, to implement your own Jupyter kernel, you should install Xeus. You can either download it with conda, or install it from sources as detailed in the &lt;a href="https://github.com/QuantStack/xeus"&gt;&lt;em&gt;&lt;strong&gt;readme&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that the installation is out of the way, let’s focus on the implementation itself.&lt;/p&gt;
&lt;p&gt;Recall that the main class for the calculator kernel must inherit from the &lt;code&gt;xinterpreter&lt;/code&gt;class so that Xeus can correctly route the messages received from the front-end.&lt;/p&gt;
&lt;p&gt;This class defines the behavior of the kernel for each message type that is received from the front-end.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kernel_info_request_impl&lt;/code&gt;: returns the information about the kernel, such as the name, the version or even a “banner”, that is a message that is prompted to console clients upon launch. This is a good place to be creative with ASCII art.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;complete_request_impl&lt;/code&gt;: checks if the code can be completed, by that we mean semantic completion, and makes a suggestion accordingly. This way the user can receive a proposition for an adequate completion to the code he is currently writing. We did not use it during our implementation as you will see later, it is safe to return a JSON with a status value only, if you do not want to handle completion.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_complete_request_impl&lt;/code&gt;: whether the submitted code is complete and ready for evaluation. For example, if brackets are not all closed, there is probably more to be typed. This message is not used by the notebook front-end but is required for the console, which shows a continuation prompt for further input if it is deemed incomplete. It also checks whether the code is valid or not. Since the calculator expects single-line inputs, it is safe to return an empty JSON object. This may be refined in the future.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inspect_request_impl&lt;/code&gt;: concerns documentation. It inspects the code to show useful information to the user. We did not use it in our case and went with the default implementation (that is to return an empty JSON object).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;execute_request_impl&lt;/code&gt;: the main function. An &lt;code&gt;execute_request&lt;/code&gt; message is sent by the front-end to ask the kernel to execute the code on behalf of the user. In the case of the calculator, this means parsing the mathematical expression, evaluating it and returning the result, as described in the next section.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Implementation of the calculator&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First things first, we need to find a way to parse mathematical expressions. To do so, we turn the user input into &lt;em&gt;&lt;strong&gt;Reverse Polish Notation&lt;/strong&gt;&lt;/em&gt; (or RPN), a name full of meaning for the wisest among our readers (or at least the oldest) who used RPN calculators in high school.&lt;/p&gt;
&lt;p&gt;The RPN, also called &lt;em&gt;&lt;strong&gt;Postfix&lt;/strong&gt;&lt;/em&gt; notation, presents the mathematical expression in a specific way : the operands go first followed by the operator. The main advantage of this notation is how it implicitly displays the precedence of operators.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Reverse Polish Notation illustration" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/images/003-0_7eozUxbipal_WV0c.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Reverse Polish Notation illustration&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The main logic of the calculator is provided by two main functions dealing respectively with parsing and evaluating the user expression and a third one for handling spaces in the expression.&lt;/p&gt;
&lt;p&gt;First we have the parsing function (&lt;code&gt;parse_rpn&lt;/code&gt;) transforming the expression into this representation. For this purpose we implement the &lt;a href="https://en.wikipedia.org/wiki/Shunting-yard_algorithm"&gt;&lt;em&gt;&lt;strong&gt;Shunting-yard algorithm&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It is based on the use of a stack data structure to change the order of the elements in the expression, depending on their type : operator, operand or parenthesis.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Transforming a user expression into RPN" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/images/004-0_kJeaW70rAkyeYh5l.jpg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Transforming a user expression into RPN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now that we have the expression turned into RPN (with spaces delimiting operands and operators) we need to do the computation. For this purpose we have the function &lt;code&gt;compute_rpn&lt;/code&gt;. Its implementation is based on a loop through a stringstream (hence the need for space delimiters) which performs operations in the right order.&lt;/p&gt;
&lt;p&gt;Note that the result is not returned as an &lt;code&gt;execute_reply&lt;/code&gt; message but is sent on a broadcasting channel instead, so that other clients to the kernel can also see it. The function &lt;code&gt;execute_reply_impl&lt;/code&gt; actually returns the status of the execution only, as you may see in the code below.&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;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interpreter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;execute_request_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;execution_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                           &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                           &lt;/span&gt;&lt;span class="nb nb-Type"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="o"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                           &lt;/span&gt;&lt;span class="nb nb-Type"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="n"&gt;store_history&lt;/span&gt;&lt;span class="o"&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;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="n"&gt;user_exprs&lt;/span&gt;&lt;span class="o"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                           &lt;/span&gt;&lt;span class="nb nb-Type"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt;&lt;span class="n"&gt;allow_stdin&lt;/span&gt;&lt;span class="o"&gt;*/&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="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&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="s2"&gt;&amp;quot;Result = &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;publish&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="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&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="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&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;this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publish_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;text&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;try&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;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spaced_code&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="n"&gt;formating_expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;result&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="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compute_rpn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parse_rpn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spaced_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                                       &lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;                                             &lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;&lt;span class="p"&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;publish_execution_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execution_counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                 &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;                                 &lt;/span&gt;&lt;span class="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class="p"&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="s2"&gt;&amp;quot;ok&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;payload&amp;quot;&lt;/span&gt;&lt;span class="p"&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="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;user_expressions&amp;quot;&lt;/span&gt;&lt;span class="p"&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="n"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jresult&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;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;runtime_error&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;err&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;nl&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;publish_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;stderr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;jresult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class="p"&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="s2"&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jresult&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;And that’s it for our calculator! It is as simple as that.&lt;/p&gt;
&lt;p&gt;Yet remember that Xeus is a library, not a kernel by itself. We still have to create an executable that gathers the interpreter and the library. This is done in a &lt;code&gt;main&lt;/code&gt; function whose implementation looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb nb-Type"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;argv&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="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt;                           &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file_name&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="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;connection.json&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="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&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="n"&gt;xeus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;xconfiguration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;config&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="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;xeus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;load_configuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&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="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interpreter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="w"&gt;                           &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interpreter_ptr&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="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;unique_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;xeus_calc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;interpreter&lt;/span&gt;&lt;span class="o"&gt;&amp;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="n"&gt;interpreter_ptr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interpreter&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="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;xeus_calc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;interpreter&lt;/span&gt;&lt;span class="o"&gt;&amp;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="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instance&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;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt;                           &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;xeus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;xkernel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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="n"&gt;xeus&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_user_name&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="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interpreter&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="n"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;First, we need to load the configuration file. To do so, we check if one was passed as an argument, otherwise, we look for the connection.json file.&lt;/p&gt;
&lt;p&gt;Then, we instantiate the interpreter that we previously set up. Finally, we can create the kernel with all that we defined beforehand. The kernel constructor accepts more parameters that allow customizing some predefined behaviors. You can find more details in the &lt;a href="https://xeus.readthedocs.io/en/latest/"&gt;&lt;em&gt;&lt;strong&gt;Xeus documentation&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;. Start the kernel and we are good to go!&lt;/p&gt;
&lt;p&gt;Now that everything is set, we can test out our homemade calculator kernel.&lt;/p&gt;
&lt;p&gt;As you can see in the demonstration below, the code displays step-by-step how the computation is done with RPN. This is done with &lt;code&gt;publish_stream&lt;/code&gt;statements, which is equivalent to &lt;code&gt;std::cout&lt;/code&gt; for the Jupyter notebook, very useful for debugging purposes.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The final result, a functional calculator!" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/building-a-calculator-jupyter-kernel/images/005-0_o8XXbdwfzxiC8Nmj.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The final result, a functional calculator!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You should now have all the information you need to implement your own Jupyter kernel. As you noticed, the Xeus library makes this task quite simple. All that you have to do is to inherit from the &lt;code&gt;xinterpreter&lt;/code&gt; virtual class and implement the functions related to the messaging protocol. Nothing more is required.&lt;/p&gt;
&lt;p&gt;This project can be found on &lt;a href="https://github.com/QuantStack/xeus-calc"&gt;&lt;em&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;. Feel free to contribute to the project if you wish to improve it, keeping in mind that xeus-calc should remain lean and simple!&lt;/p&gt;
&lt;p&gt;Note that the current implementation only supports arithmetical operators. However it can be easily extended and we may add functional support in the near future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Acknowledgments&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We would like to thank the whole &lt;a href="https://quantstack.net/the-team.html"&gt;&lt;strong&gt;QuantStack&lt;/strong&gt;&lt;/a&gt; team for their help throughout the process of making this blog post.&lt;/p&gt;
&lt;p&gt;We are also grateful to the organizers of the &lt;a href="/posts/2019/jupyter-community-workshop-building-upon-the-jupyter/"&gt;&lt;em&gt;&lt;strong&gt;Jupyter community workshop on kernels&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; as we actually started to endeavor during the event.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;About the authors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vasavan Thiru&lt;/strong&gt; is completing a master’s degree at &lt;em&gt;Sorbonne Université Pierre &amp;amp; Marie Curie&lt;/em&gt; in applied mathematics for mechanics. He is currently interning as a scientific software developer at QuantStack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thibault Lacharme&lt;/strong&gt; is finishing a master’s degree in Quantitative Finance at &lt;em&gt;Université Paris Dauphine&lt;/em&gt;. Thibault is currently on his internship as a scientific software developer at QuantStack.&lt;/p&gt;
</content><category term="C++"/><category term="kernels"/><category term="xeus"/></entry><entry><title>Jupyter Community Workshop: Jupyter Server Design and Roadmap Workshop</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/jupyter-community-workshop-jupyter-server-design-and/" rel="alternate"/><published>2019-04-30T23:58:00+00:00</published><updated>2019-04-30T23:58:00+00:00</updated><author><name>Luciano Resende</name></author><id>tag:jasongrout.github.io,2019-04-30:/medium-archive/pelican/posts/2019/jupyter-community-workshop-jupyter-server-design-and/</id><summary type="html">&lt;p&gt;We have some exciting news to share regarding the Jupyter Server Design and Roadmap Workshop as part of the Jupyter Community Workshop…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We have some exciting news to share regarding the Jupyter Server Design and Roadmap Workshop as part of the &lt;a href="/posts/2019/jupyter-community-workshops/"&gt;Jupyter Community Workshop series&lt;/a&gt; funded by &lt;a href="https://www.techatbloomberg.com/"&gt;Bloomberg&lt;/a&gt;. This workshop will take place May 16th and 17th just outside Paris France at the &lt;a href="https://www.ibm.com/fr-fr"&gt;IBM facility in Bois-Colombes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Jupyter Notebook has proven to be a tremendous tool in the scientific and analytic computing spaces. It is widely used at universities and businesses alike, enabling the ability to interactively analyze and view data in various ways, quickly and easily. However, as computational capabilities improve, the need to move Notebook kernels closer to the compute resources also increases. As a result, new requirements for how a given solution is configured and deployed are introduced.&lt;/p&gt;
&lt;p&gt;The Jupyter Server Design and Roadmap Workshop will focus on how we can bring together what has been learned over the years to address the needs of future environments, while clearly defining the separation between client and server. Items that will be discussed include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What aspects of the current Jupyter Notebook framework should be considered as “the server”?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How will extensions be exposed and consumed?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Backwards compatibility is important. How can we move forward while retaining current capabilities?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How can we introduce the ability for others to provide kernel-deployment frameworks of their own and how those frameworks are discovered?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Basic improvements that bring the server up to date (e.g., async/await — particularly in kernel life-cycle management)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;General multi-tenancy capabilities will be explored such that the server can serve more than just a single client.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How to convey kernel-specific parameters from the client, thru the server, to the kernel launch framework?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Should you be interested in joining us for this workshop, please fill out this &lt;a href="https://forms.gle/61Y2FPBwJFfhnK2G9"&gt;Google Form&lt;/a&gt;. Space is limited.&lt;/p&gt;
&lt;h3 id="acknowledgements"&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We’d like to acknowledge &lt;a href="https://www.techatbloomberg.com/"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt; for their generous support in making this workshop, and the entire series, possible. Thank you!&lt;/p&gt;
&lt;p&gt;We’d also like to thank &lt;a href="https://www.ibm.com/analytics/data-science"&gt;&lt;strong&gt;IBM&lt;/strong&gt;&lt;/a&gt; for providing the facility and hosting the Jupyter Server Design and Roadmap Workshop.&lt;/p&gt;
</content><category term="events"/><category term="Jupyter Server"/><category term="kernels"/><category term="workshops"/></entry><entry><title>Jupyter Community Workshop: Building upon the Jupyter Kernel Protocol</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/jupyter-community-workshop-building-upon-the-jupyter/" rel="alternate"/><published>2019-04-04T09:52:00+00:00</published><updated>2019-04-11T14:17:00+00:00</updated><author><name>Sylvain Corlay</name></author><id>tag:jasongrout.github.io,2019-04-04:/medium-archive/pelican/posts/2019/jupyter-community-workshop-building-upon-the-jupyter/</id><summary type="html">&lt;p&gt;We have some exciting news about the Jupyter Community Workshop on kernels!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We have some exciting news about the &lt;a href="/posts/2019/jupyter-community-workshops/"&gt;Jupyter Community Workshop&lt;/a&gt; on kernels!&lt;/p&gt;
&lt;p&gt;The workshop will be held in &lt;strong&gt;Paris&lt;/strong&gt;, France, from &lt;strong&gt;May 27th&lt;/strong&gt; to &lt;strong&gt;May 29th&lt;/strong&gt; 2019. The event is being hosted at Capital Fund Management(&lt;a href="https://www.cfm.fr//"&gt;CFM&lt;/a&gt;), in the heart of Paris.&lt;/p&gt;
&lt;p&gt;In this three-days event, we will have hands-on discussions, hacking sessions and technical presentations, with core Jupyter developers and custom language kernel authors. We will discuss the potential improvements and additions to the protocol, and work on common tools and infrastructure for testing and evaluating Jupyter kernels.&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/15PLTTyyhgzB15Qpal5uWD3oHRU3hR0_Bm-HEC59AbU8"&gt;&lt;strong&gt;Google Form&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;Why a Workshop on Jupyter Kernels?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Jupyter Kernel protocol is one of the main extension points of the Jupyter ecosystem. Dozen of language kernels have been developed by the community, and these languages can now leverage other components of the stack, such as the notebook, the console, and interactive widgets.&lt;/p&gt;
&lt;p&gt;Most Jupyter kernel authors rely on the reference documentation for the Jupyter kernel protocol, and reach out to the core team for questions on the different communication channels. One of the objectives of this event is to foster collaboration between kernel developers, on the development of common tools for testing and supporting all aspect of the protocol.&lt;/p&gt;
&lt;p&gt;Beyond implementation of the current protocol, we will discuss the potential improvements and additions to the protocol, such as the support of debugging messages. Another issue to be discussed and worked on is the ability to parameterize kernelspecs, which has been extensively discussed at the last Jupyter developer meeting in Berkeley. Parameterized kernel specs could be used for kernels requiring extra command-line arguments. These arguments could be used to pass database connection credentials, or other metadata and parameters used for e.g. parallel computing frameworks.&lt;/p&gt;
&lt;p&gt;Common infrastructure for testing kernels, call into remote kernels with the kernel gateway would also be in scope for this event.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;Acknowledgements&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This would not have been possible without the generous support provided by &lt;a href="https://www.techatbloomberg.com/"&gt;Bloomberg&lt;/a&gt;, who made this workshop series possible.&lt;/p&gt;
&lt;p&gt;We are also grateful to &lt;a href="https://www.cfm.fr/"&gt;CFM&lt;/a&gt; for gracefully hosting the Jupyter kernels community workshop.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/jupyter-community-workshop-building-upon-the-jupyter/images/001-1_3piu1NkMJz9_5sA_j2wCdA.jpeg" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
</content><category term="events"/><category term="kernels"/><category term="workshops"/></entry><entry><title>A new Python kernel for Jupyter</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/" rel="alternate"/><published>2019-01-09T09:16:00+00:00</published><updated>2019-09-30T07:04:00+00:00</updated><author><name>Martin Renou</name></author><id>tag:jasongrout.github.io,2019-01-09:/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/</id><summary type="html">&lt;p&gt;Project Jupyter aims at providing a consistent set of tools for interactive computing workflows across multiple programming languages…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Project Jupyter aims at providing a consistent set of tools for interactive computing workflows across multiple programming languages. Jupyter projects are popular at all stages of a research project from the exploration phase to the communication of results and teaching.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/001-1_ySP_XtCM_MMBawtlOOixag.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;br&gt;
&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/002-1_u5Y0cs48UCc5bmGXP2rTIA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The flagship project of Jupyter, the Notebook, and its modernized version, JupyterLab are web applications allowing the creation of documents including prose, executable code, and interactive visualizations.&lt;/p&gt;
&lt;p&gt;The kernel is the part of the backend responsible for executing code written by the user in the web application. For example, in the case of a Python notebook, execution of the code is typically handled by &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt;, the reference implementation.&lt;/p&gt;
&lt;h2 id="history"&gt;History&lt;/h2&gt;
&lt;p&gt;Initially, the &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; project included everything from the notebook web application to the kernel and server implementation. Later on, the language-agnostic parts (the notebook format, messaging protocol, qtconsole, notebook web application) were split into separate projects with a clearer scope, together forming &lt;em&gt;Project Jupyter&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The Big Split(TM) of IPython" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/003-1_PlpiXwbaiHo8VtTqcFyzGA.jpeg" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;The Big Split(TM) of IPython&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Splitting &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; into multiple packages was a good decision in that it brought a clear separation of concerns between the projects. The &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt; project was elected as the reference implementation of the Jupyter kernel protocol.&lt;/p&gt;
&lt;p&gt;This came at a cost since &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt; brought all the history and technical debt of &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; with it. Besides, Python is not a natural language for a base implementation. As a result, R and Julia kernels don’t use the reference implementation but use their own implementation of the protocol.&lt;/p&gt;
&lt;p&gt;Furthermore, the standard implementation of the Jupyter Interactive Widgets lies in the Python package &lt;a href="https://github.com/jupyter-widgets/ipywidgets"&gt;ipywidgets&lt;/a&gt;. That means that once you, as a kernel developer, implemented the Jupyter kernel protocol, you would still need to make your own backend for the interactive widgets if you want widgets support (and you should want it, honestly…).&lt;/p&gt;
&lt;p&gt;To prevent this useless duplication of effort, which harms sustainability, we set ourselves to implement a solid reusable implementation of the Jupyter kernel protocol, &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="c-implementation-of-the-kernel-protocol"&gt;C++ implementation of the kernel protocol&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/004-1_OWq9aDu1wOvYig3AalBvFg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is a C++ implementation of the Jupyter kernel protocol. It is not a kernel by itself but a library that helps kernel authoring. &lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is very helpful when developing a kernel for a language that has a C or a C++API (Python, Lua, SQL, etc.). It takes the cumbersome task of implementing the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html"&gt;Jupyter messaging protocol&lt;/a&gt; for you so you just can focus on the core interpreter tasks: executing code, inspecting, completing, etc.&lt;/p&gt;
&lt;p&gt;C++ is a good choice for a standard implementation of the protocol, it is a common denominator of most of the languages out there, it has a massive developer community and is widely adopted in the industry for performance middleware applications.&lt;/p&gt;
&lt;p&gt;With &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;, it is also very easy to reimplement core logic (server, kernel, history management etc.), if need be, by simply inheriting from &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt; library classes.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus"&gt;Xeus&lt;/a&gt; is already known for being used as a base for the C++ kernel &lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="a-xeus-based-c-kernel"&gt;A xeus-based C++ Kernel&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/005-1_vrE1i_8405140XOHfx77LA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus-cling"&gt;Xeus-cling&lt;/a&gt; is a &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;-based C++ kernel for the Jupyter Notebook. It’s very useful for teaching or learning C++. C++ has never been this interactive, thanks to the combined power of Jupyter, &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;, and &lt;a href="https://github.com/root-project/cling"&gt;cling&lt;/a&gt;. And even more impressive than executing interpreted C++, it supports interactive widgets, thanks to the &lt;a href="https://github.com/QuantStack/xwidgets"&gt;xwidgets&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;Just like &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt; is a C++ implementation of the Jupyter kernel protocol, &lt;a href="https://github.com/QuantStack/xwidgets"&gt;xwidgets&lt;/a&gt; is a C++ implementation of the Jupyter Interactive Widgets protocol, and it can be used as a base for other implementations.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/006-1_kA8toRCXwwnPmrsSIT7vhw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;You can try it right now with &lt;a href="https://mybinder.org/"&gt;binder&lt;/a&gt;, without the need of installing anything on your computer, just by clicking on &lt;a href="https://mybinder.org/v2/gh/QuantStack/xeus-cling/stable?filepath=notebooks/xcpp.ipynb"&gt;&lt;strong&gt;this link&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Interactive C++ in the Jupyter Notebook" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/007-1_OJYy6QP8HSEPUQHhyyCOcQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Interactive C++ in the Jupyter Notebook&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="a-xeus-based-python-kernel"&gt;A xeus-based Python kernel&lt;/h2&gt;
&lt;p&gt;Today, I am pleased to announce a new Python kernel based on &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;: &lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/008-1_YIXmS4xfX9MxQ6dHZLU8-g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/QuantStack/xeus-python"&gt;Xeus-python&lt;/a&gt; supports error formatting, rich display, interactive widgets, input requests, code completion, code inspection, etc.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt; and &lt;a href="https://github.com/pybind/pybind11"&gt;pybind11&lt;/a&gt;, it was very straightforward to have a first simple version of a Python kernel written in C++. It took a couple of months to have the features listed above, with a relatively small amount of code (&amp;lt; 3000 lines of C++) and a good coverage of &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt;’s features.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Simple code execution" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/009-1_2hHrN4ucrKKv68BJU9hRXQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Simple code execution&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Error formatting" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/010-1_ewhjr2dkosifi7Qky1A83g.gif" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Error formatting&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Code completion" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/011-1_LgaJ9RcNs0YZK7qaySX4NQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Code completion&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Ipywidgets support" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/012-1_3XxIdWaB7HRmRDY_PzOGaQ.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Ipywidgets support&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Just like with &lt;a href="https://github.com/QuantStack/xeus-cling"&gt;xeus-cling&lt;/a&gt;, you can try it online without the need of installing anything on your computer following &lt;a href="https://mybinder.org/v2/gh/QuantStack/xeus-python/stable?filepath=notebooks/xeus-python.ipynb"&gt;&lt;strong&gt;this link&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="why-another-python-kernel"&gt;Why another Python kernel?&lt;/h2&gt;
&lt;p&gt;Obviously, &lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt; does not cover 100% of the features of &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt;. For examples, &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; magics are not supported yet by &lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt;. However:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt; is a lot lighter than &lt;a href="https://github.com/ipython/ipykernel"&gt;ipykernel&lt;/a&gt; and &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; combined, which makes it a lot easier to implement new features on top of it. Our next goal is to augment the protocol to implement a Python debugger in JupyterLab.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;-based kernels are more versatile in that one can overload e.g. the concurrency model. This is something that &lt;a href="https://www.kitware.com/"&gt;Kitware&lt;/a&gt;’s &lt;a href="https://github.com/Slicer/SlicerJupyter"&gt;SlicerJupyter&lt;/a&gt; project takes advantage of to integrate with the Qt event loop of their Qt-based desktop application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="acknowledgments"&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;The development of &lt;a href="https://github.com/QuantStack/xeus"&gt;xeus&lt;/a&gt;, &lt;a href="https://github.com/QuantStack/xwidgets"&gt;xwidgets&lt;/a&gt;, &lt;a href="https://github.com/QuantStack/xeus-python"&gt;xeus-python&lt;/a&gt;, and related packages are led by &lt;a href="https://twitter.com/QuantStack"&gt;QuantStack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://quantstack.net/"&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/a-new-python-kernel-for-jupyter/images/013-1_1YuyD-AQ0fzVOStuxhChWw.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This development is sponsored by &lt;a href="https://www.techatbloomberg.com/"&gt;&lt;strong&gt;Bloomberg&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-author"&gt;About the Author&lt;/h2&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="http://quantstack.net/"&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="C++"/><category term="kernels"/></entry><entry><title>Introducing Jupyter Enterprise Gateway</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/" rel="alternate"/><published>2018-09-17T20:19:00+00:00</published><updated>2018-09-17T20:19:00+00:00</updated><author><name>Luciano Resende</name></author><id>tag:jasongrout.github.io,2018-09-17:/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/</id><summary type="html">&lt;p&gt;by Luciano Resende, Kevin Bates, Alan Chin&lt;/p&gt;
</summary><content type="html">&lt;p&gt;by &lt;a href="https://twitter.com/lresende1975"&gt;Luciano Resende&lt;/a&gt;, &lt;a href="https://twitter.com/kbates4"&gt;Kevin Bates&lt;/a&gt;, Alan Chin&lt;/p&gt;
&lt;p&gt;Yesterday, the Jupyter Steering Council voted to make Jupyter Enterprise Gateway a &lt;a href="https://github.com/jupyter/enhancement-proposals/blob/master/jupyter-enterprise-gateway-incorporation/jupyter-enterprise-gateway-incorporation.md"&gt;top-level Jupyter Project&lt;/a&gt;. I want to thank everyone for their contributions so far — code from my teammates at IBM and the community in general; advice from the Jupyter development team and mentors; and questions, issues, and requirements from end users.&lt;/p&gt;
&lt;p&gt;As we become an official Jupyter project, I would like to take the opportunity to give an update on the project’s progress during our incubation period.&lt;/p&gt;
&lt;h2 id="what-is-jupyter-enterprise-gateway"&gt;What is Jupyter Enterprise Gateway?&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway enables Jupyter Notebook to launch remote kernels in a distributed cluster, including Apache Spark managed by YARN, IBM Spectrum Conductor or Kubernetes.&lt;/p&gt;
&lt;p&gt;Although Enterprise Gateway is mostly kernel agnostic, it provides out of the box configuration examples for the following kernels:&lt;/p&gt;
&lt;p&gt;· Python using &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt; kernel&lt;/p&gt;
&lt;p&gt;· R using &lt;a href="https://github.com/IRkernel/IRkernel"&gt;IRkernel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;· Scala using &lt;a href="https://toree.incubator.apache.org/"&gt;Apache Toree&lt;/a&gt; kernel&lt;/p&gt;
&lt;p&gt;Jupyter Enterprise Gateway does not manage multiple Jupyter Notebook deployments, for that you should look for &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt;. Having said that, Enterprise Gateway can enable &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; to launch remote kernels as individual Kubernetes pods, providing better resource allocation and enabling better environment management as each pod can be based on different images (e.g. TensorFlow, Anaconda, etc)&lt;/p&gt;
&lt;h2 id="supported-platforms"&gt;Supported Platforms&lt;/h2&gt;
&lt;p&gt;Jupyter Enterprise Gateway currently enables remote kernels in the following platforms:&lt;/p&gt;
&lt;h3 id="distributed-kernels-in-apache-spark"&gt;Distributed Kernels in Apache Spark&lt;/h3&gt;
&lt;p&gt;Jupyter Enterprise Gateway leverages different resource managers to enable distributed kernels in Apache Spark clusters. One example shown below describes kernels being launched in YARN cluster mode across all nodes of a cluster.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway leverages Apache Spark resource managers to distribute kernels" src="https://jasongrout.github.io/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/images/001-1_oKl3bDSanz-SFqsgWsAPBw.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;&lt;em&gt;Jupyter Enterprise Gateway leverages Apache Spark resource managers to distribute kernels&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that, Jupyter Enterprise Gateway also provides some other value-added capabilities such as enhanced security and multiuser support with user impersonation.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway provides Enhanced Security and Multiuser support with user Impersonation" src="https://jasongrout.github.io/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/images/002-1_ihpHPqvgzXKepRAVZc7EIA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Enterprise Gateway provides Enhanced Security and Multiuser support with user Impersonation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="distributed-kernels-in-kubernetes"&gt;Distributed Kernels in Kubernetes&lt;/h3&gt;
&lt;p&gt;Jupyter Enterprise Gateway support for Kubernetes enables decoupling the Jupyter Notebook Server and its kernels into multiple pods. This enables running Notebook server pods with minimally necessary resources based on the workload being processed.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Jupyter Enterprise Gateway enable remote kernels on Kubernetes cluster" src="https://jasongrout.github.io/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/images/003-1__R0tS0CZLy__LmL7o5b6vg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Jupyter Enterprise Gateway enable remote kernels on Kubernetes cluster&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="jupyter-enterprise-gateway-and-jupyterhub"&gt;Jupyter Enterprise Gateway and JupyterHub&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; is a multi-user server that manages and proxies multiple instances of the single-user Jupyter notebook server. Particularly in a Kubernetes environment, Jupyter Enterprise Gateway can enable &lt;a href="https://github.com/jupyterhub/jupyterhub"&gt;JupyterHub&lt;/a&gt; to launch remote kernels as individual Kubernetes pods, providing better resource allocation and enabling better environment management as each pod can be based on different images (e.g. TensorFlow, Anaconda, etc). This has proven to be very desired, particularly when working on Deep Learning related Notebooks.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="JupyterHub and Jupyter Enterprise Gateway together in a Kubernetes cluster" src="https://jasongrout.github.io/medium-archive/pelican/posts/2018/introducing-jupyter-enterprise-gateway/images/004-1_9QJMPJLTZ04CFLc30aapTg.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;JupyterHub and Jupyter Enterprise Gateway together in a Kubernetes cluster&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="some-project-metrics"&gt;Some project metrics&lt;/h2&gt;
&lt;p&gt;The following stats have been collected from the Jupyter Enterprise Gateway GitHub repository &lt;strong&gt;during the incubation period&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;10 releases&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;12 individual contributors&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;90 Stars&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;34 Forks&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id="source-code-documentation-and-other-community-resources"&gt;Source code, documentation, and other community resources&lt;/h2&gt;
&lt;p&gt;The Jupyter Enterprise Gateway community provides multiple resources that both users and contributors can use:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source Code available at GitHub&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/jupyter/enterprise_gateway"&gt;https://github.com/jupyter/enterprise_gateway&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation available at ReadTheDocs&lt;/strong&gt;&lt;br&gt;
&lt;a href="http://jupyter-enterprise-gateway.readthedocs.io/en/latest/"&gt;http://jupyter-enterprise-gateway.readthedocs.io/en/latest/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automated builds available at Travis.CI&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://travis-ci.org/jupyter-incubator/enterprise_gateway"&gt;https://travis-ci.org/jupyter/enterprise_gateway&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Releases available at PyPi.org and Conda Forge&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://pypi.org/project/jupyter_enterprise_gateway/"&gt;https://pypi.org/project/jupyter_enterprise_gateway/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/conda-forge/jupyter_enterprise_gateway-feedstock"&gt;https://github.com/conda-forge/jupyter_enterprise_gateway-feedstock&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Related Docker Images available at Elyra organization at DockerHub&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://hub.docker.com/u/elyra/dashboard/"&gt;https://hub.docker.com/u/elyra/dashboard/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;We are eager to build an even greater community around the project, and tailor the project roadmap based on community advise.&lt;/p&gt;
&lt;p&gt;Currently, we are busy working on advancing our Kubernetes support and integration with JupyterHub.&lt;/p&gt;
&lt;p&gt;As always, we welcome questions, comments, and suggestions from users and the community in general.&lt;/p&gt;
</content><category term="kernels"/><category term="Kubernetes"/></entry></feed>