Export final code?

Folks, as you may have seen, we launched embeddable Observable notebooks this morning, and open-sourced the notebook-runtime.

On your published (or shared) notebooks, you’ll now find “ES module” and “Download tarball” links, which provide the notebook compiled down to an ES module, with all of its notebook dependencies bundled.

You can then run the notebook on your own website, or on Node.js, and observe specific variables — rendering them to the DOM, or doing something else computationally interesting with them. For the full details, see the introduction here:

This is a first stab at a minimal API for these, and I’m hoping to hear more from the folks in this thread about how we can improve it to make them more useful.

NB: @bumbeishvili — you’re quite right about wanting to hide some variables. By default, loading a notebook into the Runtime does nothing, and no cells are evaluated. You opt-in by attaching observers to the cells you’re interested in, and only those cells (and the cells that they depend on) will begin evaluation, reporting their values.


@jashkenas export, runtime, and inspector worked great in my first test drive!

Quick question about the export format: is looks like each variable in a module is represented as { inputs: [string], value: (...args) -> result }. Have you considered also including the (uncompiled) code from each variable’s cell? At the moment, it looks like variable.value.toString()–sans the wrapping function(){}–preserves the original code, but it seems like one wouldn’t want to rely on that (and that you wouldn’t want to guarantee it?).


It’s not quite the original code from each variable’s cell. The Observable compiler needs to parse out your free variables, to assign as inputs (arguments) to the function body, and convert views and mutables into pairs of variables that are referenced by name. For example: viewof chart becomes a $0 argument and variable within your compiled cell.

We’ve considered also including the uncompiled source code in the downloadable notebooks — but that would be for the purpose of creating a save/load file format, which isn’t really the goal with these initial executable notebooks.

What exactly are you hoping to use the uncompiled code for? Backups? Or something else?

This is great—congrats! Here’s my feedback on the guide:


Looks like the notebook’s version is now accessible via a header comment in the ES module:


So we can use that to get a stable, versioned URL (e.g. 189):


This seems useful, so maybe it should be mentioned in the guide?


  • Runtime.load(notebook, (cell) => <- needs an open-curly here
  • s/a just/just a/
1 Like

Thanks for the edits @shaunlebron — updated!

Cool, might be worth clarifying how to get a version number though—the only way I know is through React Devtools (search Af > state.publishVersion), but the ES Module now exposes it in the header comment:

// URL: https://beta.observablehq.com/@shaunlebron/humanae
// Title: Humanæ
// Author: Shaun Lebron (@shaunlebron)
// Version: 201  <----
// Runtime version: 1

Or maybe show it somewhere in the UI?

Yes, we definitely need a better way to show (and perhaps name) versions in the UI. That will certainly be a part of the improved notebook history / branching work we’re tackling, but we may do something smaller in the interim as well.

On this topic, it is absolutely not obvious in UI that the “share” button freezes a certain version number as “being public”, which then impacts what the next menu item does.

(On this topic) I couldn’t agree more, @Fil. That pretty much tops my list of personal peeves and priorities.

there’s no reason it wouldn’t work, but here it is…

1 Like

What exactly are you hoping to use the uncompiled code for? Backups?

@jashkenas I had two things in mind:

  1. Backups.
  2. Moving code into my own editor, for use as a non-Observable-based project (thinking of cases where code’s spread across enough notebooks/cells that copy-pasting from the UI is inconvenient). If the compiled function source is always intended to stay as close to the original code as possible, then the compiled code in the exported notebook would work fine for this… but if it were going to be compiled to a different language target (e.g. to ES5… or something less unlikely :slight_smile:) or minified, then having the uncompiled source would be useful.

Personally I would love to see an API where the whole notebook is fetched as a plain text file of uncompiled code, ideally with read/write access. Then I could edit the code in my own text editor and save changes which would sync back to the server. I could also use a wide variety of other tools such as diff tools, version control, grep, etc. etc.

Someone else might even want to make a simple FUSE filesystem for seeing all their notebooks in their file manager, etc.


@jrus looks like it is here
curl https://api.observablehq.com/document/@jrus/halton | jq .


Sorry for the newbie question, but I don’t quite grasp what I must do if, like it says there at the introduction of your most useful “Downloading and Embedding Notebooks”, I want to “save a notebook to my laptop, to show to my grandmother who lives in the internet-free woods of Nova Scotia”. I got how I can download a tarball of my code and also how to download the runtime and stylesheet that live observable’s servers, but unfortunately this seems to be not quite enough. For instance, in my offline index.html I replaced

import {Inspector, Runtime} from “https://unpkg.com/@observablehq/notebook-runtime@1?module”;


import {Inspector, Runtime} from “./runtime.js”;

where runtime.js is the file I downloaded from the url above. If I run my notebook from a server in my laptop, it runs ok if I have a network connection, but not in some internet-free woods. This particular notebook fails with

RuntimeError: md could not be resolved

and I guess any attempt to assess other observable goodies or other notebooks will similarly fail. How can I download everything I need and bring it with me to Nova Scotia?



You’re right!

I’m afraid that our current implementation of the md function, provided by the standard library, requires the marked library from Unpkg at runtime:

The notebook that I tried to run offline also depended on d3-format.

Although I could certainly imagine a version of the Observable runtime that bundled in marked, or a notebook that didn’t use it … neither of those feel like a satisfactory solution.

But I don’t think we’re going to be able to automatically determine all of the HTTP requests a notebook might make — or even just all of the names of libraries it might require — and scrape them in advance for your bundle … at least not any time too soon.

So, in lieu of a better solution in the short term, I’ve just gone ahead and reworded the introduction to remove any reference to “internet-free woods”…

Thanks, Jeremy. It’s a bummer, though. Alternatively, can you give me some pointers on manually figuring out - using the chrome inspector, say - which packages are being dynamically required by a notebook? If I could preemptively satisfy these requirements by downloading all required packages, perhaps I could cut off the internet umbilical cord. For instance, If I download marked.min.js and highlight.min.js, is it possible to tell require that these dependencies are already satisfied? Thanks again!

You could override the definition of require (and md) from the standard library to use local definitions rather than fetching them from unpkg. Overriding stdlib is discussed in another thread on the forum, I believe.

If it helps to anyone , I created minimal jsfiddle example, displaying notebook embedding technique

All cells are embedded dynamically, instead of one by one

JSFiddle - https://jsfiddle.net/5w906pL1/20/

Original notebook - https://beta.observablehq.com/@bumbeishvili/jsfiddle-embed-notebook

Update 1
If you had notebook runtime loading from this url - https://unpkg.com/@observablehq/notebook-runtime@1.2.0?module
You should replace it with following link - https://unpkg.com/@observablehq/runtime@3/dist/runtime.js


not workin anymore?

What’s not working? (You might want to start a new thread if you want to report an issue.)