JupyterSpot.com - collaborative whiteboards for Jupyter notebooks
Jupyter notebooks are the bread and butter of your typical data scientist. They're great for exploratory data analysis, prototyping, and sharing results. I found a common occurrence in data science type of meetings is someone screen-shares their notebook via Zoom and then starts scrolling through explaining their tables, plots, markdown, Latex, etc. When someone wants to ask a question about a plot that the presenter scrolled past, there's a lot of "Can you scroll up a little? No that's too far, back down a little...". If someone besides the presenter wants to point to an area of the notebook and talk about it, there's no way for them to do that.
This led me to start looking into multiplayer versions of Jupyter that strive to be like Google Docs. There are proprietary ones like CoCalc and DeepNote, then there's open-source work underway to add real time collaboration to JupyterLab using Yjs. All of these are geared towards collaborative editing of the notebook code, but what I really needed and saw a use for was collaborative viewing of the results contained in the notebook. Since notebooks usually have access to lots of valuable data, IT can be prickly about opening up the network so that others can access one of these types of notebook instances for collaborative editing. I wanted to quickly take my notebook that's either running locally or behind some corporate firewall and turn it into a shareable URL that people could interact with like they do with Figma.
What I came up with uses nbconvert to render the notebook to HTML, tldraw overlaid on top of the HTML to provide whiteboarding, and Yjs to handle synchronizing state between meeting participants. Tldraw lets everyone add freehand drawings, text, and sticky notes. You can point to things, draw, and create diagrams right on the notebook content that's being discussing instead of creating a separate blank whiteboard from Zoom, Miro, FigJam, or whatever. To turn a notebook into a whiteboard, you can add it through the site or better yet use the JupyterLab extension for one click conversion.


Each participants scroll position is added to their Yjs awareness, then a shared map keeps track of who is the scroll leader. If I want to ask my question about that plot you scrolled past, I click to become the scroll leader and then everone else has their scroll position sync with mine. If the presenter showed a plot that I didn't have enough time to parse, I can break away from syncing their scroll position and go back to review it.

Nbconvert renders everything exactly as it looks in your notebook app into a read-only HTML version and is what GitHub uses to render notebooks. Interactive plots from Bokeh, Holoviews, etc all still work, and since editing notebooks while showing them during a meeting usually doesn't go well anyway, read-only is probably good enough (eager to hear feedback on this point though). The nice thing is that nbconvert works with any ipynb file, so this approach is compatible with notebooks created in JupyterLab, VSCode, Colab, SageMaker, CoCalc, or DeepNote. Like nbviewer.org, I don't store any ipynb notebook files. The rendered HTML is cached so that nbconvert doesn't have to run each time a participant joins a room.
tldraw was on the front page of HackerNews a few months back and has a great design by Steve Ruiz that lets you use whatever CRDT you want. I modified it to handle long finite notebooks that people scroll through instead of an infinite drawing canvas. The canvas size is fixed for all participants so that the coordinates of SVG canvas elements match up to the notebook content. The width is the same for all notebooks, but the height is set from the person who creates the initial notebook canvas. This has the downside of making life harder for those who read Jupyter notebooks on mobile devices, but hopefully that's a small group. I also tried taking a huge screenshot of the notebook and adding it to the original infinite canvas version of tldraw as an image, but you lose any interactive JS plots and it kinda just looks terrible.
For the CRDT I used Yjs by Kevin Jahns. I was familiar with his work from working on RTC in JupyterLab and didn't like the alternative of using something closed source like LiveBlocks. Both Yjs and tldraw have really helpful communities, so props to Steve and Kevin for fostering that.
You can try it out the core functionality without creating an account by checking out at one of the public notebooks at https://jupyterspot.com/notebooks. For example, there's a notebook showing what Stable Diffusion thinks a collaborative Jupyter whiteboarding app looks like here: https://jupyterspot.com/notebook?id=3de4ed2c-e20d-4096-b34f-876c06b7277f
There is an extension for JupyerLab that will open your notebook inside JupyerSpot. You can install it with pip install jupyterspot
to get the functionality below.

For more information on getting started with it, check out the docs!