Alex Alemi's Blog

Plaque

Alexander A. Alemi.

I'm a big fan of the new generation of reactive notebooks that don't have internal state, projects like clerk1 for clojure, marimo for python, pluto.jl for julia and observable notebook kit2 for javascript. Each of these deal with the biggest complaint about jupyter notebooks and colab notebooks; namely that they have hidden state3.

They each use what I think is the best trick in the book for solving a problem, if you don't want to be sick to some issue, make yourself invariant to it. These projects don't look at your code linearly, from top to bottom, they instead see your code as organized into cells and resolve a dependency graph for how they all relate, ensuring that they only ever run cells when they change or once of their parent cells change.

Marimo is a really nice entry into the space, I enjoy it a lot, but it asks that I abandon my ordinary development environment4 and use theirs instead. Clerk and the just-released Observable Notebook Kit instead let you use your ordinary editor and simply watch for whenever you make updates to the code. This means you get to keep all of the niceties of the editor environment you've perfected over the years, and get to take advantage of nice tools like linters, language servers, tab completion, autocomplete, and potentially things like llm agents and claude code.

My old coworker Danijar had a nice project he called Handout, which let you build nice rendered python handouts and always executed fresh from top to bottom. You got to use your own editor and didn't have any hidden state, but this, much like marimo, required writing nonstandard python code.

Feeling like python was still missing something quite like clerk, I decided to try to roll my own. I call it Plaque, as in the ornamental tablet. It uses ordinary python code and # %% style cell boundaries like Jupytext. The bit of inspiration I had is that we can get essentially all of the benefits of a full reactive environment with very little work if we simply look for cells that change and run only those and all of the cells further down the page. With a bit more code we can actually use the python ast module to try to skip cells that don't reference any of the things we changed. With very little code we end up with a system that I've found useful enough to begin using it regularly at work.

Here's a short video demo:

If you use uv you should be able to try it out right now:

uvx plaque serve your-nb.py

Check it out and let me know what you think.