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 webSelf-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
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 notesReact corporatized the web
- it's sovereignty over the web page is in tension the DOM as a multiplayer substrate
- web extensions breaking hydration
- abstracting away the web
- obfuscation of view source (divintitis and hashed CSS names)
- the entire representation of the app is hidden away in JS
- entirely new approaches needs to be create for stuff the web already handles (e.g. CSS-in-JS)
- both it's functional ethos and the tooling it requires increases technical gatekeeping to make things on the web. Ecosystem tailwinds require you to opt in to all of this even if you're looking to use one component.
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
- Bad Apple by Valdemar Erk that embeds the Bad Apple!! shadow art video into a font. (Also check out how Valdemar made the font.)
- Fontemon by Michael Mulet that embeds an entire pokemon game.
- FontWithASyntaxHighlighter is a font by Heikki Lotvonen that does syntax highlighting for HTML, CSS, and JS.
- llama.ttf by Søren Fuglede Jørgensen that embeds an LLM into the font and predicts tries what you are going to type.
- Bovex by Tom Murphy is more of a document format than a font, but uses an LLM to "perfectly" justify text "Super Metroid speedrun documentation" style.
- Digiac that animates SVG (seems to only work in Firefox or Safari) so that you dont have to type in order to trigger the animation.
- Markdown by Wei Heng Kuan renders inline markdown.
- Numberline by Tristan Hume that nicely formats numbers.