Show HN: Observable Notebooks
beta.observablehq.com
beta.observablehq.com
Nick Strayer shows how t-SNE is similar (equivalent?) to a force-directed graph layout: https://beta.observablehq.com/@nstrayer/physics-based-t-sne
Jim Bumgardner published a tutorial that starts with drawing circles with canvas, then fibonacci spirals, then colorful sunflower seeds: https://beta.observablehq.com/@jbum/circles-spirals-and-sunf...
Kamil Slowikowski drew a lovely Barnsley Fern: https://beta.observablehq.com/@slowkow/barnsley-fern
Alan Palazzolo did a Mandelbrot fractal with randomized coloring: https://beta.observablehq.com/@zzolo/mandlebrot-set
Justin Palmer uses PROJ4JS to transform and scale a local map: https://beta.observablehq.com/@caged/local-map-projection-wi...
Any plans to add some kind of transpilation or ...?
I guess an oversimplified description would be that this is like a Jupyter Notebook specifically for JavaScript. Libraries like D3 are pre-loaded and immediately accessible. Am definitely interested in hearing the details about what it is built with and medium to long-term plans for the service.
Note that the Jupyter Notebook service generally requires you to be installing and running Python etc. on your own computer. Jumping into an Observable notebook is as easy as opening your browser and signing in via GIthub
It’s easy to use D3 within Observable, but it’s not pre-loaded or specifically built-in. Any browser library published to npm can be loaded via require (for AMD) or dynamic import (for ES modules). I’ve published a bunch of notebooks with D3 but you’ll find other ones with THREE.js, Vega-Lite, Leaflet and other libraries.
import {canvas as flood} with {height} from "@mbostock/randomized-flood-fill"
or how you define generators i = {
let i = 0;
while (true) {
yield ++i;
}
}
etcTo summarize, the body of a cell is typically either an expression or a block statement, akin to the body of an arrow function. If a cell starts with name = …, then it has a name and can be referenced by other cells. So, the name = part is specific to Observable, but the expression and block statement is normal JavaScript.
The import declaration is similar to a normal static ES import, but the with clause allows you to inject local definitions into the imported notebook.
e.g.
name = { foo: 1 }
doesn't work. I ended up doing: name = { return { foo: 1} }
but after your explanation, I guessed that this would also work: name = ({ foo: 1})I fully expect plenty of people to do some of the data-crunching in Python, Julia, R, and so on, and bring it into Observable to explore and experiment. And I also expect JavaScript's ecosystem to blossom, especially with WebAssembly & WebGL hitting the mainstream.
I’d expect more python users to bring d3 in that bring python to JavaScript.
And then it occurred to me that CRAN and NPM have a fair bit in common. Both have a package for anything you might imagine, and both have a mix of amazing and less-than-amazing quality.
https://aws.amazon.com/sagemaker/
https://cloud.google.com/datalab/
https://kyso.io (disclaimer - I'm a founder)
Do you want it to be so easy that you jump into the notebook immediately upon logging in, the hard part is that javascript will run in your browser - but python or R need a kernel running somewhere?
I figured this was like Colaboratory (https://colab.research.google.com) or Azure Notebooks (https://notebooks.azure.com/) or some other hosted-jupyter variant. There was no information about how it should work when I went to the scratchpad, and I could barely tell it was Javascript when picking one of the sample notebooks.
- cells can have names (it seems each cell can only export one name that is visible to the rest of the notebook)
- dependent cells auto-update when a source cell changes
- cells can be generators that auto update up to 60 times/sec
- cell values can be tied to UI elements easily
So instead of Jupyter's model where all cells execute in the same namespace, here you have named cells explicitly connected to each other in a graph and executed in topological order (not linear order).
The introduction covers these pretty well: https://beta.observablehq.com/@mbostock/five-minute-introduc...
I would have loved something like this when I was learning programming in middle-school decades ago.
Since it comes from the guys behind D3 it's not surprising that much of the visuals come from D3. This is really evocative, but it may limit some of the audience for this tool. I would encourage the developers to take some cues from Apple's swift playgrounds and add visuals and sliders for even some of the simpler language constructs like loops and add some "simple" wrapper functions for drawing elementary shapes. This would definitely be a shift from the intended purpose as I see it right now, but it would have the side-benefit of increasing debugability as well.
Great work - look forward to having the occasion to using it.
We haven't had time to produce as many demo notebooks as we might have liked, but these notebooks really aren't D3-specific in the slightest. They're a reactive flavor of JavaScript, and anything that produces a value or can be rendered to the DOM will work.
For example, here are a whole mess of Plotly charts: https://beta.observablehq.com/@jashkenas/plotly-js
Here are some (wild) WebGL, Three.js and regl sketches: https://beta.observablehq.com/collection/webgl
And here are some simple native inputs you can use to control your sketches: https://beta.observablehq.com/@jashkenas/inputs
Remember — a "slider" here isn't anything special. It's just a chunk of DOM that produces a number as a value. It can be replaced with anything else you can imagine that produces a number as a value. There's no API there ;)
One cool thing that Swift playgrounds let you do is advance program state through loops and other flow-control line-by-line using a slider. I think they have to have cooperation with the compiler/runtime to make this happen. (I'm also not sure how truly useful it is after a few days of learning the basics.)
I suppose you could do something similar with the native inputs but not using native syntax. What I'm thinking would be a massive undertaking I realize - it's cool that this offering generates so many "ooh and what else" ideas.
But for starters: You can do something similar to expose the internal state of loops and flow-control constructs by just using JavaScript's generators.
Here's an extremely simple example that slowly yields the value of a loop's `i` variable:
https://beta.observablehq.com/@jashkenas/flow-control-demo-w...
If you remove the delay, it'll instead yield `i` at 60fps.
Chrome 63.0.3239.132, OS X 10.13.3
I would advise looking into editing/language support early. Auto completion (for DOM API, D3 API, etc) and error checking can be very helpful to the user.
Still, congratulations to the launch (the app looks beautiful and UX is pretty solid)! When I was reading @mbostock's medium post[2] a few months ago I knew something like this will popup.
Anyway, back to building my own thing which focuses on the editing side of playgrounds, which I think is what most online ones (Codepen / JSFiddle) lack.
[0]: https://microsoft.github.io/monaco-editor
[1]: https://microsoft.github.io/language-server-protocol
[2]: https://medium.com/@mbostock/a-better-way-to-code-2b1d2876a3...
I don't fully understand how modern web security works. But "executing arbitrary javascript written by user A on website B viewed by user C" seems worrying.
Yep, Observable runs user JavaScript code, but that code has no access to your login credentials or anything from the 'application': user code runs in a sandboxed iframe on a separate host - host that doesn't have any cookies or user information. The application communicates with that iframe host through serialized postMessage information only. That's the gist of the 'user code' portion - the rest of the application has additional layers of best-practice security like very strict content security policies and so on that defend from other kinds of attacks.
If you inspect them, you'll see that those frames are hosted on observableusercontent.com instead of observablehq.com.
It's a similar security model to how raw files on GitHub are served from githubusercontent.com.
https://beta.observablehq.com/@ontouchstart/test
to confirm that the code is running in an iframe from host https://static.observableusercontent.com
(Are you sure it's a good idea to perpetuate the status quo? It's hard to get anyone to pay attention to what you've made unless you're already known. That seems a bit unfair to the next generation, and rather the opposite of oldschool hacker ethos.)
The challenge is helping people understand what something is and why it is valuable as quickly as possible. In this case, the fact that the team behind this are Mike Bostock (d3), Jeremy Ashkenas (Backbone, CoffeeScript) and Tom MacWright (Mapbox Studio) feels very relevant to getting me excited about the project.
It's such a strong product that I imagine word of mouth (plus how well it demos) will do that job for them though. This was just my first thought on loading up he page in Mobile Safari (and then realizing what it actually was).
1) I can match my previous knowledge of related projects to get an idea of what this project is trying to be, and likely have that be accurate.
2) in an age where any random half-baked idea has a polished marketing page and lofty ambitions, it's a noise filter for "these people have completed projects that have actually been used". If anything, on a page that doesn't name-drop, I'd trust a bare-bones, nocss landing page more.
Feedback: I instantly understood that it was something similar to Jupyter, and my very first mental question was "I hope it's in JS", and this took a surprisingly long few seconds and two page navigations to establish -- I expected the choice of language to be on the splash page.
Anyway, very well done, and it's quite clear to me that JS is going to trounce Python for nearly everything long-term. The JS ecosystem is just an unstoppable freight train.
"Observable: An Earthquake Globe in Ten Minutes" https://www.youtube.com/watch?v=uEmDwflQ3xE
The finished notebook from the screencast (which you can fork): https://beta.observablehq.com/@jashkenas/earthquakes
And a fancier version that spins, reuses the canvas element instead of re-rendering it from scratch, projects the quakes as circles on the surface of the sphere, and offers a bunch of other options to configure (like if you'd like to chart the past day, week or month of quakes): https://beta.observablehq.com/@jashkenas/quakespotter-0-1
You can change which cell is focused using Option-Tab ⌥⇥ and Shift-Option-Tab ⇧⌥⇥. You can also click on the asterisk (*) in the left margin to edit any cell.
Curious as to know how developers view the notebook paradigm? I feel like there is a sense of pride in working from the command line or vi, so wonder how these UI heavy approaches are received by the js/python/scala community.
Regarding D3, I haven't tried that out yet, although I've used the `folium` Python package, which generates leaflet.js code you can put on a website. Otherwise, maybe check out [Jupyter Dashboards](https://github.com/jupyter/dashboards), whose interactive widgets seem to be in the same vein as OP.
I think it is really cool, and I have mountains of respect for Mike for his contributions to the field of data visualization/javascript, but my feeling is that this is more suited for sharing "notebooks" that are interactive and also to potentially allow for collaborative notebook building/demos. In essence, this is a giant evolution over his previous work on bl.ocks.org (https://bl.ocks.org/mbostock)
https://mybinder.org/v2/gh/twosigma/beakerx/0.12.0?filepath=...
Mike, Jeremy, and Tom, this looks awesome. I'm looking forward to put it through the paces.
How so?
Ive found it pretty straightforward to convert matplotlib plots to gif/mp4 for presentation (note: this can be done automatically within the notebook), and plotly is pretty good for truly interactive stuff.
I'm currently working on a JS-powered geospatial exploration app that is meant to be run either standalone or embedded into a Jupyter notebook. We often run into questions around how much the embeddable app should be scriptable or how much user control to allow. This would seemingly solve many of our needs.
building a map with a few input controls that alter the data or presentation (colors, layers, filtering, aggregation)
with R + shiny + leafletThat said, I tried to create a demo application and it seems like the require / exports / global situation isn't 100% yet. Any tips from the team on what to do here?
The idea is intriguing, however it is non trivial to pass information from JS to Python and vice versa. You can do that, but it messes with Jupyter's event system and breaks the usual notebook workflow. Maybe this has changed for the better, but at that time this stuff (IPython widgets I think) was heavily in flux.
How would I load in data that is stored on my harddrive (not web addressable) without having to run my own server or go through the file selector popup box each time. User JS can't just read the harddisk (a requirement for web security). If I have to run my own server then a major selling point of this is gone.
I work with large tabled data and very often use vectorised functions using numpy or pandas, working on entire columns with a fantastically simple interface. A = B * x, for arrays B, scalars x returning a new array A. Is there something as beautiful available, working around the lack of operator overloading in JS? I've only seen string abuse like p('A') = p('B*x'). Auh.
And, yep - something like pandas for JavaScript would also be nice... also stay tuned.
Edit 1: Deploying the fix now...
Edit 2: ... and we're back.
...but not always
A few comments:
- Probably needs some more documentation. Maybe a quick reference for the API that's available and a list of keystrokes. I will admit though, I bailed halfway through the tutorials so I could start playing with it.
- I'd like to be able to drag and drop cells (or otherwise rearrange them). I created my page, but then wanted to better organize it. Making new cells and cut/pasting content was annoying.
- The Opt-Return thing is killing me. It executes the cell in Jupyter and splits the cell into two in Observable. I can't think of a reason I'd want to split a cell in the middle. I believe splitting in the middle will always result in two cells with syntax errors. Perhaps just always create and focus a new, empty cell?
- It'd be nice if there was an easy way to delete a cell. Currently, I'm emptying it and then joining with the previous via option-del, which concatenates cells. I believe joining two cells with content will always result in a syntax error, so I'm not sure the utility of that.
- The samples were useful, especially for learning how d3 fits into this. I ended up breaking up a long function (to build the d3 graph) into multiple cells, taking cues from the samples. (I also learned that d3 has changed a little since v3.)
It would also be nice to be able to rename variables (i.e. refactor), but that's not a critical feature.
Would be curious to hear plans to monetize it.
especially useful with data visualization/exploration, but also helps a lot if you have a complex sequence where it's easy to get lost and forget to recompute something. there's a youtube link in the comments here which explains a lot.
Although I see how that would enable rapid iteration and make e.g. creating nice visualizations much easier, I don't think it should be mandatory. The user may want to control the state their notebook and variables are in manually.
Can you share an example where you’d want that behavior? Spreadsheet cells don’t ask you when they update themselves.
Jupyter is a web server. Notebooks run in the browser. iPython the kernel can run on the command line in addition to the browser.
> 1. You don’t have to install anything, so running (or forking) someone else’s notebook is as simple as clicking a link
Jupyter lets you clone notebooks with the click of a button.
Also, you can run Javascript from a Notebook cell, and there are Python libraries that interface with the JS visualization libraries, like Plotly.
Of course JS gives you full flexibility for talking to the DOM and graphics APIs, but web apps are not the main focus of data science. The web is just one possible means to presentation or collaboration.
Or was it just not an issue, because you figured 1) not having to installing anything and 2) reactive would be enough to win converts?
There's no shortcut for deleting items on the "help" section. No WYISWYG editor for that either. So if you want to delete a bulletpoint you accidentally made, it just shows up as "undefined"
There's not a way to bind your own hotkeys. Some of default hotkeys get overridden by my own set of hotkeys, so this isn't too helpful
I'm not a big fan of the UI/UX. I see where its going, collapsing items you don't want to see. But this gets rather tedious to always do everytime. There should be a default setting that lets you bind whether pins are defaulted or not.
When I saw this app, I thought Tonic must have gone through another rebranding. Wrong again! This just looks more geared for data visualization and collaboration though. Would be curious to see if Tonic/RunKit (or iPython even) were used as inspiration.
I do have a question: for notebooks linking to outside notebooks: is there anything that prevents breakages? Ie external notebook variable name changes or is completely removed, would parts of my notebook relying on that one then also fail? Is there some kind of public versioning of notebooks so that I can refer to a value at notebook ref,v1?
It's not ready yet, but we're hoping to roll out import pinning for notebooks soon.
Each notebook is versioned — every time you make a change and re-run the cell, a version is saved to the server. And every time you publish, that's a publication of a specific version of the notebook.
Our plan is to pin the version of any code you import from outside the notebook, including require(npm-module), import {value} from "other-notebook", and even the version of the Observable standard library that was current when you wrote the notebook.
Hopefully, that should provide a good foundation for robust notebooks that don't break as libraries change over time. Of course, you'll be able to re-pin a new version if you want to upgrade something.
A notebook already runs in a sandboxed iframe, and is able to load its dependencies there (libraries, data, values from other notebooks). So we're in quite good shape to be able to do that soon.
One final question/request (for which I'd happily pay for!) : it would be awesome to have a path away from Observable's infra if desired. Say I _really_ want to host a particular notebook locally: is something like that planned? I know this is not a trivial feature since notebooks can call other notebooks, but I'd love to develop stuff on Observable knowing that should the worst happen and it doesn't exist anymore, I can run it all locally on my webpage.
We’ve been scrambling to get things ready enough for this initial launch, but Mike managed to get the Standard Library open sourced this morning: https://github.com/observablehq/notebook-stdlib
One of the next pieces we'd like to open-source is the Notebook Runtime — All of the JS that you need to take a blank webpage, and host a notebook within it.
After that, there's some file format details to figure out — hopefully a notebook can just be published and consumed as a standard ES Module. But then we'll be most of the way there.
Also, where are the notebook stored? Can I get an offline copy? Wasn't obvious from a quick look.
The visualizations especially good as a feature to promote it.
im sure im not alone id love to create pretty charts and would wow a customer.. But cant justify spending the cycles to fully learn d3 etc..
Mike: if you're reading this: thanks a million!
But....
IMO, the project name is a disaster.
If successful it will muddy the water on searches for RxJS (and other Rx) observables.
Please do like the Amber.js team did when they learned of a name comflict and rebrand as Ember.js. That would be awesome.