Chris Shank

About Me

Hi, I'm Chris! ✌🏻

I explore alternative branches of computing that are communal, prosocial and decorporatized. I'm particularly interested in how we can nudge the web in this direction.

I'm an independent researcher, which means my open research is directly funded by people like you! Your support means everything, so check out sponsorship page to get inside access to what I'm exploring.

Web page as a REPL

Spurred by Omar Rizwan's tweet about using the browser as a REPL to build an we application from the about:blank page, I started thinking about what it would look like for a website to let you directly modify and persist it's source. This can be a tricky thing to do for a traditional web page. Of course you can pop open the dev tools on any website, change some styles, update some DOM, but there is no good way to persist the changes you made. And even if you were able to, those changes could clash with the way that webpage was architected (e.g. React, etc). The core tension here is the web's single-origin security really dictates that web page was never yours to change, there's a reason we call it “view source”. Another challenge is how do you modify a webpage that is loading tens or even hundreds of separate files.

Luckily there's a lot of prior art about alternative approaches to this called “self-contained HTML pages” or “self-modifying HTML pages. My first encounter was Cristobal's self-modify HTML notes. I later learned this is how Tiddly wiki has been working for decades. At LIVE 24' I saw Gilad Bracha present about the self-contained environment called Ampleforth. Tom Larkworthy has recently been working on a self-contained reactive notebook called LopeCode.

Towards an editable web

Self-contained JavaScript module system

The JavaScript module system only works with external JavaScript files. There are open issues to script exports property and importing/exporting inline module scripts

allow inline scripts to expose exports. In the meantime, Tom Larkworthy figured out a way to do this in browsers today. It takes advantage of three things; the fact that script tags don't execute when they have a non-standard type attribute, importmaps, and Blob URLs. So first we need a new type of script tag, let's call it hash-module and we need to give it an id so other modules can reference it by name. Here's an example:

<script type="hash-module" id="sum">
  export const sum = (a, b) => a + b;
</script>

<script type="hash-module" id="main">
  import { sum } from '#sum';
  console.log(sum(1, 2));
</script>

It doesn't take a lot of JavaScript to instantiate hash modules:

const imports = {};

document.querySelectorAll('script[type=hash-module]').forEach((module) => {
  imports['#' + module.id] = URL.createObjectURL(new Blob([module.text], { type: 'application/javascript' }));
});

const importmap = document.createElement('script');
importmap.type = 'importmap';
importmap.text = JSON.stringify({ imports }, null, 2);

document.head.appendChild(importmap);

importmap.remove();
        

The boot loader must be an inline script because in some browsers wont let you define importmaps after the JS module system runs. This means all hash modules need to be defined before the inline script.

Why do I call it a hash module?

Well these modules are referring to each other by ids in a similar way that URL hashes scroll to the element with that id on page load. Furthermore, adding a hash in the import statement helps differentiate between hash modules and regular JS modules that exist in separate files. As I previously mentioned these script tags aren't executed because browsers don't execute script tags with a non-standard type attribute so we need a process that can boot load them up. Luckily it's a pretty simple process: grab all of the has modules, create a blob URL for each, and create an importmap that maps the id of the module to the Blob URL.

Why immediately add then remove the importmap from the DOM?

Well script tags execute immediately after being added to the DOM so nothing changes if they're removed. It's important in the context of a self-contained HTML file that we don't litter it with things that are transient like this importmap.

Towards an editable web

The web started off as a readonly medium, networked infrastructure to share and read documents. In the mid 90s, browsers started experimenting the ability to “view source”. Gary Ing recounts that it started as a “fun way to let people surfing the web to see this code, if they wanted to.” It's honestly a miracle that the idea of view source has rooted itself so deeply into the web given its unprecedented nature and its tensions with corporate interests. But “view source” also highlights the readonly nature into the web, there's a reason that it's not called “edit source”. When you navigate to a web page you are merely a spectator of its content and view source lets you peak into its plumbing, if you happen to be technically savvy enough to understand the source. The browser might have downloaded a copy of a web page that someone else created, but it's not a copy you own because it's not a copy that you can annotate and modify.

It's worth mentioning that the web has become way more interactive since its conception, but it's a different kind of authorship and ownership than I'm interested in. It's a form of permissionful write. The author of the web page had to write a HTML form, program some JavaScript, or implement Drag and Drop to let you modify their web page. You needed the author's permission in order to do that. And it's usually just the content that you are allowed to edit, not the underlying source code. What would the web could look like if it had a model for permissionless write?

Well the first challenge is around persistence, web pages are quite transient. The copy of the web page that the browser downloaded is lost when that page is navigated away from or the tab is closed. Even if you could permissionlessly modify a web page that page is still not yours. For the better or worse, to gain ownership of a web page you need to have a local copy stored on your file system.

Definition of User Agent - WAI UA Wiki User Agent Accessibility Guidelines (UAAG) 2.0

The DOM is a graph

The DOM is more than a tree of HTML elements. It also defines relationships between elements that aren't tree-like. HTML forms convey this. The <form> element has a elements property to let you access all of the associated form controls. Those could be contained deeply within the <form> but also outside of the it via form attribute. Form controls also have a form property that their associated with. From this we can see that HTML forms encode some kind of bi-directional graph.

This framing is useful perspective shift because Custom HTML elements let us encode new data structures into the DOM.

Custom HTML elements let us encode new data structures into the DOM.

The DOM is a graph which can be extended with custom HTML elements. For example, take a look at this very web page! The custom <wiki-note> element encodes a graph of notes that it links too and notes that link back to it. We can programmatically traverse through the graph with the links and backlinks properties. It's trivial to implement using query selector.

Evergreen notes

Andy Matuschak's evergreen notes

React corporatized the web

Fonts as a computational substrate

Font formats like OpenType have scripting built-in to handle character shaping (i.e. layout and ligatures). This opens up all kinds of possible things we can do with fonts. Fonts are infrastructural across computing environments so we can adversarially transform any environment that lets someone select a font. Imagine a plaintext editor or <textarea> transformed into a rich markdown editor by selecting a font.

Examples of esoteric fonts

Prompting for permissions is a bad cross-application security model