The site uses cookies that you may not want. Continued use means acceptance. For more information see our privacy policy.

Working on a Local Single Page Application.

If only my editor’s highlighter supported template literals.

I’ll surely post about it more detail as I get it fully built, but I thought I’d write about it as I’ve been working on it.

As I’ve written before (diehealthy.org: 19 September 2020: “How I Track Games to Buy”) about how I track games to buy, using bookmarks, it occurred to me that I’d like something a little more defined than using bookmark titles to store data. And when I say a little, I mean that. I don’t want a relational database (though there is one built in the browser, if I want to use it). I don’t want a server to configure.

I do want a simple web application, often called a SPASingle-Page Application (Wikipedia: “Single-page application”). But as I said, no server. That makes it an LSPA, or Local Single-Page Application. And single-page really means single file, as in one HTML document that contains all the markup, all the code, and all the styles in one package.

The secret of the modern browser is that it has a ton of functionality that it doesn’t get credit for. While (unfortunately) the behavior of localStorage in the file: schema is undefined, at present Firefox makes it a per-file access, so as long as you persist the filename and path, you get the storage back. To be a little more sure of things, you can export the JSON data as a file, and import it from a file.


I’ve used one-off HTML files for other projects before, including years and years ago for some of my Computer Science classes where choice-of-language was wide-open, but it’s been awhile. In general, the browser is a nice platform to write for, but it’s underdeveloped in terms of making these kind of one-file applications widespread. To be fair, there are concerns about users downloading random HTML files and opening up vulnerabilities, but the general shape of browser security seems to guard decently against it such that enabling more local, serverless, in-browser applications would be useful.

People use spreadsheets for all sorts of data storage and simple applications because it’s got all those tools. They could be doing basically the same thing with a browser. (That’s in fact what I am doing with a browser.) In some cases, the numerical prowess of a spreadsheet will make their task easier. In other cases, the web-awareness of the browser makes my task a lot easier.

One place where a spreadsheets take the one-file ideal slightly further: they store the data in the application. Fair enough.


I looked at various libraries to bootstrap building the editing side of things from a JSON schema. There are a bunch of them, but none seemed very easy to integrate or to do what I wanted with it. It took me less time to build the equivalent for my own purposes than I spent looking at and trying to understand the umpteen JSON-to-forms Javascript libraries. And for mine I don’t add dependencies like underscore.js or jQuery.

On the other hand, I’ve spent a bit of my times dusting my ability to write Javascript, wondering what’s canonical these days. There are proper classes with constructors now (but you don’t have to use them). There are things like Map()s that are better than plain objects in some ways, but aren’t as nice to use in other ways.

To save a file, you have to:

  1. Create an anchor (A).
  2. Create a Blob.
  3. Create an object URL for the Blob.
  4. Add the URL as the anchor’s href attribute.
  5. Add the desired filename as the anchor’s download attribute.
  6. Add the anchor to the document.
  7. Call click() on the anchor (the actual download occurs).
  8. Clean up.

Seems like a lot of extra work for a very usual thing. (A roughly similar process to load from a file, except using an input with type of "file" and some other specifics.)


Anyhow, the one feature I’m relying on an extension for that Bookmarks have out of the box is the ability to get the title and URL in a single action. Mozilla Addons: Hiroaki Nakamura: “Format Link” is an add-on I already use to do that for other cases. But it seems like it’s something browsers should support, given how much we all use the web. We still need computers that understand our most-used forms of data as logical objects, but until then there’s nice extensions to help us.

With that ability, the main pieces of data for tracking a game are available with a paste, which isn’t too much more than simply adding a bookmark. The rest of the data was already stuff I was filling in by hand, but it will soon be into my application rather than cramming it all in the bookmark’s title.

Anything else you’d want from a server-provided service can be built locally and only using Javascript. Given I don’t expect to have hundreds of thousands of games to track, I don’t even need to use a relational database. The browser can handle filtering, sorting, search.

For heavier uses, like media databases, solely relying on a LSPA might not be enough power or might not be able to handle some things like creating thumbnails, but for many other uses, it’s a powerful model that I’d like to see more support and frameworks for people to make use of, especially non-programmers or people with only a little knowledge.

Hands Reaching Out in Darkness

Please vote in the election on 8 November 2022.

Hands reach out in darkness. Feel the air for heat, not cold, for moisture and movement, for anything solid they could hold.

Fingers, three bones hinged together, wired, wrapped in muscle and skin, capped off with a nail. They pivot and flex through night, anxious, cautious, greedy, and strong. Straighten, bend, straighten, in the unknown these evolved worms, structured machines, must have some purpose.

Some form fists, little heart-sized balls ready to inflict. They wrap themselves over the fear, hardened against escape: keep it close, tickling the palm.

Others bend in strange shapes, foreign signs of ritual meant to conjure health or peace or even light, in the darkness.

These hands do not know much. Do they have eyes? Can light be found to bounce off skin and nail and crease and knuckle, and to tunnel through a pupil? Can a clap cause air to shamble toward a curl of cartilage, down into a series of tiny hairs?

They swim and sway like moths, like windy leaves, like derelicts on stormy seas. None finds a touch. Not one thing to press or prod. Not one thing to punch. Not in that ink.

Average Users and Accessibility

Or, why to design around misuse-cases.

The longer I use consumer computing, the more I’m convinced that it shouldn’t require much knowledge at all. They have familiarity with some aspects of consumer computers, while they are on average more ignorant of its underlying nature, and those who are interested will learn more about the bits behind the pixels. But it’s clear that we shouldn’t expect the user to know too much, as many will not.

It’s quite a challenge, and it deserves an example. One that comes to mind is Unicode and emoji. The average user saw something like Wordle and gladly spammed Twitter with a bunch of emoji boxes that made those tweets ugly to the ears of users with screenreaders. And in general, the misuse of emoji and unicode characters for purposes other than they’re intended are harmful to accessibility.

But the average user that just wants to make a cool looking thing doesn’t know about accessibility. And while learning about it is good, and it can lead to people supporting accessibility-first not just in computing but in general—which has the knock-on effect of making the world better regardless of abilities—we generally recognize people will only learn about what they want. If they find it interesting, they’ll learn, but most people are too busy or uncurious.

So we necessarily should design with the ideas:

  1. Lots of people will want to misuse the system.
  2. Fewer people will want to abuse the system.
  3. We need to design to defend against [2], but allow for [1].

(The distinction between misuse and abuse is one of malice. Using a fork to stick a note onto a cork board is misuse. Using a fork as a weapon is abuse.)

Going back to the unicode and emoji example, what does that mean? Let’s take the Wordle example. They want to paste their scores, and we don’t want to screw over screenreaders, and we also don’t want to have to design some extra input (e.g., alt text) that lots of people won’t use. What we want to do is have some way to let the text-meaning be included alongside the emoji, so the paster doesn’t have to do anything, but the screenreader can make the distinction. This would likely involve the use of the Javascript Clipboard API, adding an extra MIME type (something like text/srfriendly?), but where the consuming application requests paste data of both the text/plain and (if available) text/srfriendly.

The result could be the consuming application getting the collated data to enable the display version, but allowing screenreaders to fall back on the superior version for their application.

(We should still have alt text for fallback for images, of course. Indeed, there should be an initiative to let us embed alt text in the image file rather than needing to add it separately. [Actually! See Lexdis 2.0: 27 June 2022: “Exploring the embedding of accessible image descriptions into image metadata”, which links to and quotes from an announcement by the International Press Telecommunications Council (IPTC) that its photo metadata standard now includes fields for textual descriptions. Neat.] It would be more convenient for most people to add the alt text at the point when they create the image than when they embed it. (Though we should still let them provide an alternative alt text at point of embed. It can be prefilled with the version from the image.))


The addition of a MIME type for applications would help those cases, but contexts where users frequently misuse unicode should also allow for the alternate input to be added, whether by the original user, by tools meant to offer cleaned versions, or through crowd suggestions. The average misuser of unicode isn’t going to stop doing that. Only by giving the option to misuse while still filling in the gap misuse creates will cover all cases.