Understanding Observable - Downloading HTML & JS

Hi there !

I’m learning D3 (and HTML and JavaScript and CSS at the same time, it’s a lot of pain !)
My goal is to create a local HTML & JS page on my laptop.

I was working on small examples that I’m able to download and make them work directly locally like here https://www.d3-graph-gallery.com/graph/line_brushZoom.html

So far so good, but I’ve found interesting example like here : https://observablehq.com/@d3/focus-context

But ! I tried to hack it a bit, but quickly realized that all code is not shown, like for example how is the “width” variable initialized and lot of other stuff -> I have far more problems than only D3, now it’s JavaScript also that I have to “reverse engineer”.
Also after the HTML div and svg tag to create, etc, etc …

My question:
Is there a way to simply create a local HTML & JS page with just the graph of “focus-context” page ?

If Observable doesn’t work this way, for this specific example, could you provide me a simple HTML+JS template that I could hack locally for “Focus+Context”?

Thanks !

1 Like

Hi @funkyfonk.

The easiest way to embed your chart in your website is to click the three dots next to the cell and then click Embed Code.

Screen Shot 2020-03-05 at 8.11.15 AM

If you copy and paste this into your webpage, your chart will be appear.

Here’s an example:

And here’s it running live:


If you want to self-host the code for your notebook, you can click Download Code in the notebook menu, which will give you everything you need (and see the contained README for instructions).

For more on how to download and embed notebooks, please read our notebook:

As for the width variable, that’s provided by Observable’s standard library. It’s reactive, so whenever the window resizes, its value updates to reflect the new width, and any cells referencing the width will re-run. Hence, charts that are based on the reactive width are automatically responsive! You can see that in the above Focus + Context example (both on Observable and when embedded) by resizing the window.

More generally, Observable is a superset of JavaScript that adds reactivity. You can read more about the differences here:

It’s also possible to rewrite the Observable examples in vanilla JavaScript, but it requires re-implementing all the stuff that reactivity gives you “for free”, like adding event listeners for anything that can change (such as the width and the focus). If you’re just learning JavaScript, you’ll hopefully find it easier to rely on Observable’s reactivity rather than managing it yourself, and you can use our tiny open-source runtime to embed your visualizations anywhere on the web.


Thanks Mike, this is really clear, detailed and responsive (like width, haha !)

I’ll take the time to test and understand your explanations.

Thanks a lot !

1 Like

Hi @mbostock and everyone,

I followed your instructions and everything is super smooth now, I can run all the code of focus-context locally.

One last thing which is not local and I still need internet : the code (probably runtime.js ?) calls two URLs on the internet:

I had the idea of :
-> hacking runtime.js by modifying cdn dot jsdelivr dot net by (Not sure it’s a good idea, sounds a bit dirty ?)
-> trying to do it properly, (like ), but how could I prevent runtime.js to make those calls ?

So to sum-up, how to be able to use Observable fully without internet now from this point ?

Thanks !

There are a couple ways to do this.

First you can pass a resolver to redefine where a required library comes from.

const runtime = new Runtime(new Library((name, base) =>
  name === "d3@5"
    ? d3
    : Library.resolve(name, base)

Second you can override d3 to avoid the call to require in the first place.

main.redefine("d3", d3);

Can I use the same approach for File Attachments? I haven’t had success bundling a notebook project due to import.meta

When you download a notebook, it does that automatically: you download the file attachments as well, so that they are self-hosted.

File attachments aren’t (typically) loaded via require, so specifying a resolver won’t change how they are loaded, but you could use module.redefine to override a cell that loads a file attachment.

If your bundler doesn’t support import.meta, there might be a plugin for your bundler that helps you rewrite the code, or you could edit the downloaded code to point to a different location.