<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: mike-bostock</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/mike-bostock.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2024-03-05T21:12:48+00:00</updated><author><name>Simon Willison</name></author><entry><title>Observable Framework 1.1</title><link href="https://simonwillison.net/2024/Mar/5/observable-framework-1-1/#atom-tag" rel="alternate"/><published>2024-03-05T21:12:48+00:00</published><updated>2024-03-05T21:12:48+00:00</updated><id>https://simonwillison.net/2024/Mar/5/observable-framework-1-1/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/observablehq/framework/releases/tag/v1.1.0"&gt;Observable Framework 1.1&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Less than three weeks after 1.0, the 1.1 release adds a whole lot of interesting new stuff. The signature feature is self-hosted npm imports: Framework 1.0 linked out to CDN hosted copies of libraries, but 1.1 fetches copies locally and then bundles that code with the deployed static site.&lt;/p&gt;

&lt;p&gt;This works by using the acorn JavaScript parsing library to statically analyze the code and find all of the relevant imports.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mbostock/status/1765076085249921413"&gt;@mbostock&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/npm"&gt;npm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable-framework"&gt;observable-framework&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="npm"/><category term="observable"/><category term="mike-bostock"/><category term="observable-framework"/></entry><entry><title>Interesting ideas in Observable Framework</title><link href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#atom-tag" rel="alternate"/><published>2024-03-03T17:54:21+00:00</published><updated>2024-03-03T17:54:21+00:00</updated><id>https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#atom-tag</id><summary type="html">
    &lt;p&gt;Mike Bostock, &lt;a href="https://observablehq.com/blog/observable-2-0"&gt;Announcing: Observable Framework&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Today we’re launching &lt;a href="https://observablehq.com/product"&gt;Observable 2.0&lt;/a&gt; with a bold new vision: an open-source static site generator for building fast, beautiful data apps, dashboards, and reports.&lt;/p&gt;
&lt;p&gt;Our mission is to help teams communicate more effectively with data. Effective presentation of data is critical for deep insight, nuanced understanding, and informed decisions. Observable notebooks are great for ephemeral, &lt;em&gt;ad hoc&lt;/em&gt; data exploration. But notebooks aren't well-suited for polished dashboards and apps.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href="https://observablehq.com/framework/"&gt;Observable Framework&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are a lot of &lt;em&gt;really&lt;/em&gt; interesting ideas in Observable Framework.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#static-site-dashboards"&gt;A static site generator for data projects and dashboards&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#javascript-in-markdown"&gt;JavaScript in Markdown&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#everything-reactive"&gt;Everything is still reactive&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#only-code-you-use"&gt;Only include the code that you use&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#cache-data-at-build"&gt;Cache your data at build time&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#comparison-to-observable-notebooks"&gt;Comparison to Observable Notebooks&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://simonwillison.net/2024/Mar/3/interesting-ideas-in-observable-framework/#change-in-strategy"&gt;A change in strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="static-site-dashboards"&gt;A static site generator for data projects and dashboards&lt;/h4&gt;
&lt;p&gt;At its heart, Observable Framework is a static site generator. You give it a mixture of Markdown and JavaScript (and potentially other languages too) and it compiles them all together into fast loading interactive pages.&lt;/p&gt;
&lt;p&gt;It ships with a full featured hot-reloading server, so you can edit those files in your editor, hit save and see the changes reflected instantly in your browser.&lt;/p&gt;
&lt;p&gt;Once you're happy with your work you can run a build command to turn it into a set of static files ready to deploy to a server - or you can use the &lt;code&gt;npm run deploy&lt;/code&gt; command to deploy it directly to Observable's own authenticated sharing platform.&lt;/p&gt;
&lt;h4 id="javascript-in-markdown"&gt;JavaScript in Markdown&lt;/h4&gt;
&lt;p&gt;The key to the design of Observable Framework is the way it uses JavaScript in Markdown to create interactive documents.&lt;/p&gt;
&lt;p&gt;Here's what that looks like:&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-mh"&gt;# &lt;span class="pl-en"&gt;This is a document&lt;/span&gt;&lt;/span&gt;

Markdown content goes here.

This will output 1870:

&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt;
&lt;span class="pl-c1"&gt;34&lt;/span&gt; &lt;span class="pl-k"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;55&lt;/span&gt;
&lt;span class="pl-s"&gt;```&lt;/span&gt;

And here's the current date and time, updating constantly:

&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt;
&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-en"&gt;Date&lt;/span&gt;(now)
&lt;span class="pl-s"&gt;```&lt;/span&gt;

The same thing as an inline string: ${new Date(now)}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Any Markdown code block tagged &lt;code&gt;js&lt;/code&gt; will be executed as JavaScript in the user's browser. This is an &lt;em&gt;incredibly&lt;/em&gt; powerful abstraction - anything you can do in JavaScript (which these days is effectively anything at all) can now be seamlessly integrated into your document.&lt;/p&gt;
&lt;p&gt;In the above example the &lt;code&gt;now&lt;/code&gt; value is interesting - it's a special variable that provides the current time in milliseconds since the epoch, updating constantly. Because &lt;code&gt;now&lt;/code&gt; updates constantly, the display value of the cell and that inline expression will update constantly as well.&lt;/p&gt;
&lt;p&gt;If you've used Observable Notebooks before this will feel familiar - but notebooks involve code and markdown authored in separate cells. With Framework they are all now part of a single text document.&lt;/p&gt;
&lt;p&gt;Aside: when I tried the above example I found that the &lt;code&gt;${new Date(now)}&lt;/code&gt; inline expression displayed as &lt;code&gt;Mon Feb 19 2024 20:46:02 GMT-0800 (Pacific Standard Time)&lt;/code&gt; while the &lt;code&gt;js&lt;/code&gt; block displayed as &lt;code&gt;2024-02-20T04:46:02.641Z&lt;/code&gt;. That's because inline expressions use the JavaScript default string representation of the object, while the &lt;code&gt;js&lt;/code&gt; block uses the Observable &lt;code&gt;display()&lt;/code&gt; function which has its own rules for how to display different types of objects, &lt;a href="https://github.com/observablehq/inspector/blob/main/src/inspect.js"&gt;visible in inspect/src/inspect.js&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="everything-reactive"&gt;Everything is still reactive&lt;/h4&gt;
&lt;p&gt;The best feature of Observable Notebooks is their &lt;em&gt;reactivity&lt;/em&gt; - the way cells automatically refresh when other cells they depend on change. This is a big difference to Python's popular Jupyter notebooks, and is the signature feature of &lt;a href="https://marimo.io/"&gt;marimo&lt;/a&gt;, a new Python notebook tool.&lt;/p&gt;
&lt;p&gt;Observable Framework retains this feature in its new JavaScript Markdown documents.&lt;/p&gt;
&lt;p&gt;This is particularly useful when working with form inputs. You can drop an input onto a page and refer its value throughout the rest of the document, adding realtime interactivity to documents incredibly easily.&lt;/p&gt;
&lt;p&gt;Here's an example. I ported one of my &lt;a href="https://observablehq.com/@simonw/datasette-downloads-per-day-with-observable-plot"&gt;favourite notebooks&lt;/a&gt; to Framework, which provides a tool for viewing download statistics for my various Python packages.&lt;/p&gt;
&lt;p&gt;The Observable Framework version can be found at &lt;a href="https://simonw.github.io/observable-framework-experiments/package-downloads"&gt;https://simonw.github.io/observable-framework-experiments/package-downloads&lt;/a&gt; - source code &lt;a href="https://github.com/simonw/observable-framework-experiments/blob/main/docs/package-downloads.md?plain=1"&gt;here on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/pypi-dashboard.gif" alt="Animated demo showing PyPI download stats for Datasette projects - as I switch a select menu between sqlite-utils and csv-diff and shot-scraper the displayed chart updates to match." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;This entire thing is just 57 lines of Markdown. Here's the code with additional comments (and presented in a slightly different order - the order of code blocks doesn't matter in Observable thanks to reactivity).&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-mh"&gt;# &lt;span class="pl-en"&gt;PyPI download stats for Datasette projects&lt;/span&gt;&lt;/span&gt;

Showing downloads for &lt;span class="pl-s"&gt;**&lt;/span&gt;${packageName}&lt;span class="pl-s"&gt;**&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It starts with a Markdown &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; heading and text that shows the name of the selected package.&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt; echo
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;packageName&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;view&lt;/span&gt;(&lt;span class="pl-smi"&gt;Inputs&lt;/span&gt;.&lt;span class="pl-c1"&gt;select&lt;/span&gt;(packages, {
  value&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;sqlite-utils&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
  label&lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Package&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
}));
&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This block displays the select widget allowing the user to pick one of the items from the &lt;code&gt;packages&lt;/code&gt; array (defined later on).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Inputs.select()&lt;/code&gt; is a built-in method provided by Framework, described in the &lt;a href="https://observablehq.com/framework/lib/inputs"&gt;Observable Inputs&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;view()&lt;/code&gt; function is new in Observable Framework - it's the thing that enables the reactivity, ensuring that updates to the input selection are acted on by other code blocks in the document.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;packageName&lt;/code&gt; is defined with &lt;code&gt;const&lt;/code&gt; it becomes a variable that is visible to other &lt;code&gt;js&lt;/code&gt; blocks on the page. It's used by this next block:&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt; echo
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;data&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;d3&lt;/span&gt;.&lt;span class="pl-en"&gt;json&lt;/span&gt;(
  &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;https://datasette.io/content/stats.json?_size=max&amp;amp;package=&lt;span class="pl-s1"&gt;&lt;span class="pl-pse"&gt;${&lt;/span&gt;packageName&lt;span class="pl-pse"&gt;}&lt;/span&gt;&lt;/span&gt;&amp;amp;_sort_desc=date&amp;amp;_shape=array&lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;/span&gt;
);&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we are fetching the data that we need for the chart. I'm using &lt;code&gt;d3.json()&lt;/code&gt; (all of D3 is available in Framework) to fetch the data from a URL that includes the selected package name.&lt;/p&gt;
&lt;p&gt;The data is coming from &lt;a href="https://datasette.io/"&gt;Datasette&lt;/a&gt;, using the Datasette JSON API. I have a SQLite table at &lt;a href="https://datasette.io/content/stats"&gt;datasette.io/content/stats&lt;/a&gt; that's updated once a day with the latest PyPI package statistics via a convoluted series of GitHub Actions workflows, &lt;a href="https://simonwillison.net/2021/Jul/28/baked-data/#baked-data-datasette-io"&gt;described previously&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Adding &lt;code&gt;.json&lt;/code&gt; to that URL returns the JSON, then I ask for rows for that particular package, sorted descending by date and returning the maximum number of rows (1,000) as a JSON array of objects.&lt;/p&gt;
&lt;p&gt;Now that we have &lt;code&gt;data&lt;/code&gt; as a variable we can manipulate it slightly for use with Observable Plot - parsing the SQLite string dates into JavaScript &lt;code&gt;Date&lt;/code&gt; objects:&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt; echo
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;data_with_dates&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;data&lt;/span&gt;.&lt;span class="pl-en"&gt;map&lt;/span&gt;(&lt;span class="pl-k"&gt;function&lt;/span&gt;(&lt;span class="pl-smi"&gt;d&lt;/span&gt;) {
  d.date = d3.&lt;span class="pl-en"&gt;timeParse&lt;/span&gt;(&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;%Y-%m-%d&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;)(d.date);
  return d;
})
```&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This code is ready to render as a chart. I'm using &lt;a href="https://observablehq.com/plot"&gt;Observable Plot&lt;/a&gt; - also packaged with Framework:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```js echo
Plot.plot({
  y: {
    grid: true,
    label: `${packageName} PyPI downloads per day`
  },
  width: width,
  marginLeft: 60,
  marks: [
    Plot.line(data_with_dates, {
      x: "date",
      y: "downloads",
      title: "downloads",
      tip: true
    })
  ]
})
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we have one cell that lets the user pick the package they want, a cell that fetches that data, a cell that processes it and a cell that renders it as a chart.&lt;/p&gt;
&lt;p&gt;There's one more piece of the puzzle: where does that list of packages come from? I fetch that with another API call to Datasette. Here I'm using a SQL query executed against the &lt;a href="https://datasette.io/content"&gt;/content&lt;/a&gt; database directly:&lt;/p&gt;
&lt;div class="highlight highlight-text-md"&gt;&lt;pre&gt;&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt; echo
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;packages_sql&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;select package from stats group by package order by max(downloads) desc&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;```&lt;/span&gt;
&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;span class="pl-en"&gt;js&lt;/span&gt; echo
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;packages&lt;/span&gt; &lt;span class="pl-k"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;fetch&lt;/span&gt;(
  &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;https://datasette.io/content.json?sql=&lt;span class="pl-s1"&gt;&lt;span class="pl-pse"&gt;${&lt;/span&gt;&lt;span class="pl-c1"&gt;encodeURIComponent&lt;/span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;    packages_sql&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;  )&lt;span class="pl-pse"&gt;}&lt;/span&gt;&lt;/span&gt;&amp;amp;_size=max&amp;amp;_shape=arrayfirst&lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;/span&gt;
).&lt;span class="pl-c1"&gt;then&lt;/span&gt;((&lt;span class="pl-smi"&gt;r&lt;/span&gt;) &lt;span class="pl-k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;r&lt;/span&gt;.&lt;span class="pl-en"&gt;json&lt;/span&gt;());
&lt;span class="pl-s"&gt;```&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;_shape=arrayfirst&lt;/code&gt; is a shortcut for getting back a JSON array of the first column of the resulting rows.&lt;/p&gt;
&lt;p&gt;That's all there is to it! It's a pretty tiny amount of code for a full interactive dashboard.&lt;/p&gt;
&lt;h4 id="only-code-you-use"&gt;Only include the code that you use&lt;/h4&gt;
&lt;p&gt;You may have noticed that my dashboard example uses several additional libraries - &lt;code&gt;Inputs&lt;/code&gt; for the form element, &lt;code&gt;d3&lt;/code&gt; for the data fetching and &lt;code&gt;Plot&lt;/code&gt; for the chart rendering.&lt;/p&gt;
&lt;p&gt;Observable Framework is smart about these. It implements lazy loading in development mode, so code is only loaded the first time you attempt to use it in a cell.&lt;/p&gt;
&lt;p&gt;When you build and deploy your application, Framework automatically loads just the referenced library code from the &lt;a href="https://www.jsdelivr.com/"&gt;jsdelivr CDN&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="cache-data-at-build"&gt;Cache your data at build time&lt;/h4&gt;
&lt;p&gt;One of the most interesting features of Framework is its &lt;a href="https://observablehq.com/framework/loaders"&gt;Data loader&lt;/a&gt; mechanism.&lt;/p&gt;
&lt;p&gt;Dashboards built using Framework can load data at runtime from anywhere using &lt;code&gt;fetch()&lt;/code&gt; requests (or wrappers around them). This is how Observable Notebooks work too, but it leaves the performance of your dashboard at the mercy of whatever backends you are talking to.&lt;/p&gt;
&lt;p&gt;Dashboards benefit from fast loading times. Framework encourages a pattern where you build the data for the dashboard at deploy time, bundling it together into static files containing just the subset of the data needed for the dashboard. These can be served lightning fast from the same static hosting as the dashboard code itself.&lt;/p&gt;
&lt;p&gt;The design of the data loaders is beautifully simple and powerful. A data loader is a script that can be written in &lt;em&gt;any&lt;/em&gt; programming language. At build time, Framework executes that script and saves whatever is outputs to a file.&lt;/p&gt;
&lt;p&gt;A data loader can be as simple as the following, saved as &lt;code&gt;quakes.json.sh&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the application is built, that filename tells Framework the destination file (&lt;code&gt;quakes.json&lt;/code&gt;) and the loader to execute (&lt;code&gt;.sh&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This means you can load data from any source using any technology you like, provided it has the ability to output JSON or CSV or some other useful format to standard output.&lt;/p&gt;
&lt;h4 id="comparison-to-observable-notebooks"&gt;Comparison to Observable Notebooks&lt;/h4&gt;
&lt;p&gt;Mike introduced Observable Framework as &lt;em&gt;Observable 2.0&lt;/em&gt;. It's worth reviewing how the this system compares to the original Observable Notebook platform.&lt;/p&gt;
&lt;p&gt;I've been a huge fan of Observable Notebooks for years - &lt;a href="https://simonwillison.net/tags/observable/"&gt;38 blog posts and counting&lt;/a&gt;! The most obvious comparison is to Jupyter Notebooks, where they have some key differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Observable notebooks use JavaScript, not Python.&lt;/li&gt;
&lt;li&gt;The notebook editor itself isn't open source - it's a hosted product provided on &lt;a href="https://observablehq.com/"&gt;observablehq.com&lt;/a&gt;. You can export the notebooks as static files and run them anywhere you like, but the editor itself is a proprietary product.&lt;/li&gt;
&lt;li&gt;Observable cells are &lt;em&gt;reactive&lt;/em&gt;. This is the key difference with Jupyter: any time you change a cell all other cells that depend on that cell are automatically re-evaluated, similar to Excel.&lt;/li&gt;
&lt;li&gt;The JavaScript syntax they use isn't quite standard JavaScript - they had to invent a new &lt;code&gt;viewof&lt;/code&gt; keyword to support their reactivity model.&lt;/li&gt;
&lt;li&gt;Editable notebooks are a pretty complex proprietary file format. They don't play well with tools like Git, to the point that Observable ended up implementing their own custom version control and collaboration systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Observable Framework reuses many of the ideas (and code) from Observable Notebooks, but with some crucial differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notebooks (really documents) are now &lt;strong&gt;single text files&lt;/strong&gt; - Markdown files with embedded JavaScript blocks. It's all still reactive, but the file format is much simpler and can be edited using any text editor, and checked into Git.&lt;/li&gt;
&lt;li&gt;It's &lt;strong&gt;all open source&lt;/strong&gt;. Everything is under an ISC license (OSI approved) and you can run the full editing stack on your own machine.&lt;/li&gt;
&lt;li&gt;It's all just standard JavaScript now - &lt;strong&gt;no custom syntax&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="change-in-strategy"&gt;A change in strategy&lt;/h4&gt;
&lt;p&gt;Reading the tea leaves a bit, this also looks to me like a strategic change of direction for Observable as a company. Their previous focus was on building great collaboration tools for data science and analytics teams, based around the proprietary Observable Notebook editor.&lt;/p&gt;
&lt;p&gt;With Framework they appear to be leaning more into the developer tools space.&lt;/p&gt;
&lt;p&gt;On Twitter &lt;a href="http://twitter.com/observablehq"&gt;@observablehq&lt;/a&gt; describes itself as "The end-to-end solution for developers who want to build and host dashboards that don’t suck" - the Internet Archive copy &lt;a href="https://web.archive.org/web/20231003212202/https://twitter.com/observablehq"&gt;from October 3rd 2023&lt;/a&gt; showed "Build data visualizations, dashboards, and data apps that impact your business — faster."&lt;/p&gt;
&lt;p&gt;I'm excited to see where this goes. I've limited my usage of Observable Notebooks a little in the past purely due to the proprietary nature of their platform and the limitations placed on free accounts (mainly the lack of free private notebooks), while still having enormous respect for the technology and enthusiastically adopting their open source libraries such as &lt;a href="https://observablehq.com/plot/"&gt;Observable Plot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Observable Framework addresses basically all of my reservations. It's a fantastic new expression of the ideas that made Observable Notebooks so compelling, and I expect to use it for all sorts of interesting projects in the future.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pypi"&gt;pypi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/d3"&gt;d3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jupyter"&gt;jupyter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable-framework"&gt;observable-framework&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable-plot"&gt;observable-plot&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="javascript"/><category term="open-source"/><category term="pypi"/><category term="d3"/><category term="jupyter"/><category term="observable"/><category term="mike-bostock"/><category term="observable-framework"/><category term="observable-plot"/></entry><entry><title>Observable Plot</title><link href="https://simonwillison.net/2021/May/4/observable-plot/#atom-tag" rel="alternate"/><published>2021-05-04T16:28:34+00:00</published><updated>2021-05-04T16:28:34+00:00</updated><id>https://simonwillison.net/2021/May/4/observable-plot/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://observablehq.com/@observablehq/plot"&gt;Observable Plot&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is huge: a brand new high-level JavaScript visualization library from Mike Bostock, the author of D3—partially inspired by Vega-Lite which I’ve used enthusiastically in the past. First impressions are that this is a big step forward for quickly building high-quality visualizations. It’s released under the ISC license which is “functionally equivalent to the BSD 2-Clause and MIT licenses”.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mbostock/status/1389611128339144704"&gt;@mbostock&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/visualization"&gt;visualization&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/d3"&gt;d3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable-plot"&gt;observable-plot&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="visualization"/><category term="d3"/><category term="observable"/><category term="mike-bostock"/><category term="observable-plot"/></entry><entry><title>Quoting Mike Bostock</title><link href="https://simonwillison.net/2021/Feb/23/mike-bostock/#atom-tag" rel="alternate"/><published>2021-02-23T22:55:32+00:00</published><updated>2021-02-23T22:55:32+00:00</updated><id>https://simonwillison.net/2021/Feb/23/mike-bostock/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://observablehq.com/@mbostock/10-years-of-open-source-visualization"&gt;&lt;p&gt;When building a tool, it’s easy to forget how much you’ve internalized: how much knowledge and context you’ve assumed. Your tool can feel familiar or even obvious to you while being utterly foreign to everyone else. If your goal is for other people to use the darn thing — meaning you’re not just building for yourself, or tinkering for its own sake (which are totally valid reasons) — you gotta help people use it! It doesn’t matter what’s possible or what you intended; all that matters is whether people actually succeed in practice.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://observablehq.com/@mbostock/10-years-of-open-source-visualization"&gt;Mike Bostock&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/d3"&gt;d3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="d3"/><category term="mike-bostock"/></entry><entry><title>D3 Projection Comparison</title><link href="https://simonwillison.net/2019/Mar/10/d3-projection-comparison/#atom-tag" rel="alternate"/><published>2019-03-10T22:58:09+00:00</published><updated>2019-03-10T22:58:09+00:00</updated><id>https://simonwillison.net/2019/Mar/10/d3-projection-comparison/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://observablehq.com/@d3/projection-comparison"&gt;D3 Projection Comparison&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fun Observable notebook that lets you compare any two out of D3’s 96 (!) geographical projections of the world.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mbostock/status/1104823044986429440"&gt;@mbostock&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/geo"&gt;geo&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/d3"&gt;d3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;&lt;/p&gt;



</summary><category term="geo"/><category term="d3"/><category term="observable"/><category term="mike-bostock"/></entry><entry><title>Observable Beta</title><link href="https://simonwillison.net/2018/Jan/31/observable/#atom-tag" rel="alternate"/><published>2018-01-31T16:46:38+00:00</published><updated>2018-01-31T16:46:38+00:00</updated><id>https://simonwillison.net/2018/Jan/31/observable/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://beta.observablehq.com/"&gt;Observable Beta&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Observable just released their beta, and it’s quite something. It’s by Mike Bostock (d3), Jeremy Ashkenas (Backbone, CoffeeScript) and Tom MacWright (Mapbox Studio). The easiest way to describe it is Jupyter notebooks for JavaScript supporting reactive programming—so code is evaluated as you type and you can add interactive widgets (like sliders and canvas views)  to construct explorable visualizations on the fly.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=16274686"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jeremy-ashkenas"&gt;jeremy-ashkenas&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/d3"&gt;d3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jupyter"&gt;jupyter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/observable"&gt;observable&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mike-bostock"&gt;mike-bostock&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tom-macwright"&gt;tom-macwright&lt;/a&gt;&lt;/p&gt;



</summary><category term="javascript"/><category term="jeremy-ashkenas"/><category term="d3"/><category term="jupyter"/><category term="observable"/><category term="mike-bostock"/><category term="tom-macwright"/></entry></feed>