Reading List
The most recent articles from a list of feeds I subscribe to.
State of HTML 2023 now open!
tl;dr the brand new State of HTML survey is finally open!
Take State of HTML 2023 Survey
Benefits to you:
- Survey results are used by browsers to prioritize roadmaps β the reason Google is funding this. Time spent thoughtfully filling them out is an investment that can come back to you tenfold in the form of seeing features you care about implemented, browser incompatibilities being prioritized, and gaps in the platform being addressed.
- In addition to browsers, several standards groups are also using the results for prioritization and decision-making.
- Learn about new and upcoming features you may have missed; add features to your reading list and get a list of resources at the end!
- Get a personalized score and see how you compare to other respondents
- Learn about the latest trends in the ecosystem and what other developers are focusing on
While the survey will be open for 3 weeks, responses entered within the first 9 days (until October 1st) will have a much higher impact on the Web, as preliminary data will be used to inform Interop 2024 proposals.

The State of HTML logo, designed by Chris Kirk-Nielsen, who I think surpassed himself with this one!
Background
This is likely the most ambitious Devographics survey to date. For the past couple of months, Iβve been hard at work leading a small product team spread across three continents (2am to 8am became my second work shift π ). We embarked on this mission with some uncertainty about whether there were enough features for a State of HTML survey, but quickly found ourselves with the opposite problem: there were too many, all with good reasons for inclusion! To help weigh the tradeoffs and decide what makes the cut we consulted both the developer community, as well as stakeholders across browsers, standards groups, community groups, and more.
We even designed new UI controls to facilitate collecting the types of complex data that were needed without making the questions too taxing, and did original UX research to validate them. Once the dust settles, I plan to write separate blog posts about some of these.
FAQ
Can I edit my responses?
Absolutely! Do not worry about filling it out perfectly in one go. If you create an account, you can edit your responses for the whole period the survey is open, and even split filling it out across multiple devices (e.g. start on your phone, then fill out some on your desktop, etc.) Even if youβre filling it out anonymously, you can still edit responses on your device for a while. You could even start anonymously and create an account later, and your responses will be preserved (the only issue is filling it out anonymously, then logging in with an existing account).
So, perhaps the call to action above should beβ¦
Start State of HTML 2023 Survey
Why are there JS questions in an HTML survey?
For the same reason there are JS APIs in the HTML standard: many JS APIs are intrinsically related to HTML. We mainly included JS APIs in the following areas:
- APIs used to manipulate HTML dynamically (DOM, form validation, etc.)
- Web Components APIs, used to create custom HTML elements
- APIs used to create web apps that feel like native apps (e.g. Service Workers, Web App Manifest, etc.)
If you donβt write any JS, we absolutely still want to hear from you! In fact, I would encourage you even more strongly to fill out the survey: we need to hear from folks who donβt write JS, as they are often underrepresented. Please feel free to skip any JS-related questions (all questions are optional anyway) or select that you have never heard these features. There is a question at the end, where you can select that you only write HTML/CSS:

Is the survey only available in English?
Absolutely not! Localization has been an integral part of these surveys since the beginning. Fun fact: Nobody in the core State of HTML team is a native English speaker.

Each survey gets (at least partially) translated to over 30 languages.
However, since translations are a community effort, they are not necessarily complete, especially in the beginning. If you are a native speaker of a language that is not yet complete, please consider helping out!
What does my score mean?
Previous surveys reported score as a percentage: βYou have heard or used X out of Y features mentioned in the surveyβ. This one did too at first:

This was my own score when the survey first launched, and I created the darn survey π Our engineer, Sacha who is also the founder of Devographics got 19%!
These were a lot lower for this survey, for two reasons:
- It asks about a lot of cutting edge features, more than the other surveys. As I mentioned above, we had a lot of difficult tradeoffs to make, and had to cut a ton of features that were otherwise a great fit. We errβed on the side of more cutting edge features, as those are the areas the survey can help make the most difference in the ecosystem.
- To save on space, and be able to ask about more features, we used a new compact format for some of the more stable features, which only asks about usage, not awareness.
Here is an example from the first section of the survey (Forms):
However, this means that if you have never used a feature, it does not count towards your score, even if you have been aware of it for years.
It therefore felt unfair to many to report that youβve βheard or usedβ X% of features, when there was no way to express that you have heard 89 out of 131 of them!
To address this, we changed the score to be a sum of points, a bit like a video game: each used feature is worth 10 points, each known feature is worth 5 points.
Since the new score is harder to interpret by itself and only makes sense in comparison to others, we also show your rank among other participants, to make this easier.

My score after the change. If you have already taken the survey, you can just revisit it (with the same device & browser if filled it in anonymously) and go straight to the finish page to see your new score and ranking!
I found a bug, what should I do?
Please file an issue so we can fix it!
Acknowledgements
This survey would not have been possible without the hard work of many people. Besides myself (Lea Verou), this includes the rest of the team:
- Engineering team: Sacha Greif, Eric Burel
- UX research & data science team: Shaine Rosewel Matala, Michael Quiapos, Gio Vernell Quiogue
- Our logo designer, Chris Kirk-Nielsen
And several volunteers:
- LΓ©onie Watson for accessibility feedback
- Our usability testing participants
- β¦and all folks who provided early feedback throuhgout the process
Last but not least, Kadir Topal made the survey possible in the first place, by proposing it and securing funding from Google.
Thank you all! ππΌ
Press coverage (selected)
- Turns out I know less about HTML than I thought! π - Kevin Powell (Video)
- Are you an HTML expert? Find out with the new State of HTML 2023 survey - dev.to
- Chrisβ Corner: Things I Totally Didnβt Know About That I Learned From Taking the State of HTML 2023 Survey
- Frontend Focus
- CSS Weekly
- The HTML Blog
A little book and its author, out in the world.
So! You Deserve a Tech Unionβs been getting a little attention lately! Hereβs a short roundup.
TinyPilot: Month 38
New here?
Hi, I’m Michael. I’m a software developer and the founder of TinyPilot, an independent computer hardware company. I started the company in 2020, and it now earns $60-80k/month in revenue and employs seven other people.
Every month, I publish a retrospective like this one to share how things are going with my business and my professional life overall.
Highlights
- I failed to sell recurring TinyPilot license subscriptions.
- I realized I made TinyPilot way too configurable.
- I thought I’d been investing poorly into TinyPilot’s development, but writing this retrospective made me realize I’m mostly on track.
Goal grades
At the start of each month, I declare what I’d like to accomplish. Here’s how I did against those goals:
gokrazy is really cool
When you deal with Linux, you end up hearing about "distributions" as different "flavors" of Linux combined with a bunch of other tools. This is mostly true, but it's slightly missing the forest for the trees.
Consider this famous and often misunderstood quote by Richard Stallman:
I'd just like to interject for a moment. What you're referring to as Linux is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux.Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.

Many pages of ink have been spilled over analyzing this quote, and a lot of them fall short of really getting at the heart of the matter. What this actually means is something like this:
By itself, Linux is useless. It does boot the system, it does interface with hardware, but without a bunch of other tools, it's not very useful. It's like a car without a steering wheel, or a boat without a rudder. It does something, but it's not very useful. The real value of things like the GNU project, systemd, openrc and other tools in that vein is that they make Linux useful. They make it into a complete system that you can use to do things. They are the proverbial steering wheel and rudder in the metaphor.
Most Linux systems on the face of the planet are built with GNU tools and utilities. In order to compile the Linux kernel, you need to use GCC. In order to run ls to list files in the current directory, you need to use GNU coreutils. Every dynamically linked program uses glibc for performing basic system interactions like writing to files or opening network sockets. Everything is built on top of the GNU toolset. This is why Stallman is so adamant about calling it GNU/Linux. It's not that he's trying to take credit for Linux, it's that he's trying to give credit to the GNU project for making Linux useful.
However, there's a lot of room for nuance here. For example, Alpine Linux is a Linux distribution that uses musl libc instead of glibc and busybox instead of GNU coreutils. It's still a Linux distribution, but it doesn't use the GNU toolset. It's still a Linux distribution, but it's not GNU/Linux.
So, what is a Linux distribution? It's a collection of tools that make Linux useful. It's a collection of tools that make Linux into a complete system. It's not a "flavor" of Linux (though this conceptually can exist with alternative kernels like the Zen kernel patchset), it's a system that just so happens to make Linux useful.
As a counter-argument, consider the reason why Linux runs on more devices worldwide than there are people: Android. Android does use the Linux kernel, but it doesn't use any GNU tools in the stack at all. You can't take programs that are compiled against other Linux distributions and run them on Android. You can't take programs that are compiled against Android and run them on other Linux distributions.
I'm going to argue that Android is not a Linux distribution unto itself. Android is a Linux implementation. It uses the Linux kernel, but that's where the similarities with the rest of the ecosystem end. Android is its own little world where there's just enough system tools to get the system running, but once you get into the UI, it's a completely different world. It's a completely different ecosystem. It's a completely different operating system.
/bin/sh, it's a Linux distribution.gokrazy
gokrazy is a Linux implementation that I've used off and on for a few years now. It's a very interesting project because everything on the system is written in Go save the kernel. The init process is in Go (and even listens over HTTP to handle updates!), every userland process is written in Go, and even the core system services are written in Go.
Out of the box a gokrazy install comes with these basic tools:
- The
initprocess that is mandated to be the parent of all userland processes by the Linux kernel. - A DHCP client that automatically configures the network interface.
- A NTP client that automatically sets the system clock.
- A little tool to save randomness from the kernel to a file so that it can be used to seed the random number generator on boot (because the Raspberry Pi doesn't have a robust hardware random number generator)
That's it. Everything else from the web UI to A/B update logic is written in Go. It boots in literal seconds, uses an insanely small amount of RAM out of the box, and runs with nearly zero overhead. When you configure your gokrazy install to run additional software, you do so by adding the Go command path to a configuration file and then updating to trigger a reboot into the new version.
Here's an example of what my gokrazy virtual machine's file tree looks like:
/ # tree etc gokrazy user
etc
βββ breakglass.authorized_keys
βββ gokr-pw.txt
βββ gokrazy
β βββ sbom.json
βββ hostname
βββ hosts
βββ http-port.txt
βββ https-port.txt
βββ localtime
βββ machine-id
βββ resolv.conf -> /tmp/resolv.conf
βββ ssl
βββ ca-bundle.pem
gokrazy
βββ dhcp
βββ heartbeat
βββ init
βββ ntp
βββ randomd
user
βββ breakglass
βββ fbstatus
βββ qemu-guest-kragent
βββ serial-busybox
βββ tailscale
βββ tailscaled
βββ waifud-gok-agent
That is the entire system. It's all stripped down to these few programs, configuration files, and one symlink for DNS resolution. This is a very minimal system, and it's all you need to run statically linked Go programs. It's very easy to deploy your own services to it too. It's probably the easiest platform I know of that lets you just deploy a Go binary and have it run as a service, automatically restarting when it crashes.
The tooling
When I used gokrazy back in the day, you had to use a command line called gokr-packer that you passed a bunch of command line flags to with information about all the Go programs you wanted to run on the machine, configuration for those programs, and any other meta-information like where the update tool should push the image to. It was a bit of a pain to use, but it worked. Recently the gok tool was added to the project, and this has been revolutionary when it comes to using and administrating gokrazy installs.
Essentially, gok is a wrapper around the existing gokr-packer logic with a JSON file to store your configuration details. It's a lot easier to use, understand, and automate. You don't have to remember command line flags or maintain unwieldy scripts. You just edit a JSON file and push updates with gok update. It's amazingly simple.
Setting up a gokrazy machine
As an example, I'm going to show you how to install a bunch of tailnet addons to a gokrazy machine. I'm also going to assume that you don't have a gokrazy install set up yet, so we'll need to install it. To do this, we'll need to do a few simple things:
- Install the
goktool. - Create your
gokconfiguration. - Install Tailscale on the machine.
- Create your "seed" image with
gok overwrite. - Boot it on your Raspberry Pi or VM.
- Push any updates to the image to the machine with
gok update.
First, let's install the gok tool. In order to do this, you need to have the Go toolchain installed. Once you have that, you can run go install to install the gok tool:
go install github.com/gokrazy/tools/cmd/gok@main
~/go/bin is in your $PATH variable so that you can run it by the name gok instead of ~/go/bin/gok.Next, create a new gokrazy configuration with gok new:
gok new -i casa
This will create a configuration named casa (cf: Spanish for "house") in ~/gokrazy/casa. This is where all of your configuration files will live. You can edit the configuration file with gok edit:
gok edit -i casa
If you are making a virtual machine
If you are making a virtual machine, you will need to override the kernel and firmware packages. You can do this by adding the following to your configuration file:
{
// ...
"KernelPackage": "github.com/rtr7/kernel",
"FirmwarePackage": "github.com/rtr7/kernel",
// ...
}
You will need to prefix the gok overwrite and gok update commands with GOARCH=amd64 to ensure that Go builds x86_64 binaries instead of ARM binaries:
GOARCH=amd64 gok update -i casa
If you don't do this, you will get arm64 binaries being built. This may require manual recovery of your virtual machine.
Let's make our lives easier by installing Tailscale on the machine. By default, gokrazy will announce its hostname over DHCP, which usually makes most consumer routers pick it up and then lets you ping it by name. When you have MagicDNS enabled, Tailscale can take over this logic and prevent you from accessing the machine by name.
However, Tailscale is written in Go and doesn't require any of the services that most Linux distributions provide in order to function. It's a perfect fit for gokrazy. You can install it with gok add:
gok add tailscale.com/cmd/tailscaled
gok add tailscale.com/cmd/tailscale
And be sure to add the mkfs service to create a persistent partition on /perm:
gok add github.com/gokrazy/mkfs
Next, fetch an auth key from the admin console and make sure you check that it's reusable. Then, add the following to your configuration file under the PackageConfig block:
{
// ...
"PackageConfig": {
// ...
"tailscale.com/cmd/tailscale": {
"CommandLineFlags": [
"up",
// paste your key here!
"--authkey=tskey-auth-hunter2-hunter2hunter2hunter2"
]
},
// ...
}
// ...
}
tailscale up flags you want here, such as --advertise-exit-node if you want to use your gokrazy machine as an exit node.This will make your machine automatically connect to Tailscale on boot.
Next, we need to create our "seed" image with gok overwrite. First, figure out what the device node for your SD card is. On Linux, you can do this with lsblk:
lsblk
And then look for the one that has the same size as your SD card. In my case, it's /dev/sdd. Once you have that, you can run gok overwrite:
gok overwrite --full /dev/sdd
However if you want to write the image to a file (such as if you are doing mass distribution or making a VM image), you need to use gok overwrite with a file instead of a device node. This will create a 16 GB image:
gok overwrite -i casa --full gokrazy.img --target_storage_bytes 17179869184
Once you have your image, you can write it to your SD card with dd (or balenaEtcher) or import it into your virtual machine hypervisor of choice.
Once you have your image written to your SD card, you can boot it on your Raspberry Pi or VM.
breakglass as a tool of last resort to modify things, but you only have a very minimal subset of busybox to work with, so it should be avoided if at all possible.Once you have your machine booted and it responds to pings over Tailscale, you can open its HTTP interface in your browser. If you called your machine casa, you can open it at http://casa. It will prompt you for a username and password. Your username is gokrazy, and the password is near the top of your config.json file. When you log in, you'll see a screen like this:
This is the gokrazy web UI. It lets you see the status of your machine and any logs that are being generated by your applications. You can also start, stop, and restart any of your applications from here. It's a very simple UI, but it's fantastic for debugging and monitoring.
Tailnet addons
Now that we have a Gokrazy system up and running, let's add some programs to it! I'm going to list a couple tailnet addons that give your tailnet superpowers. These are all written in Go, so they're a perfect fit for gokrazy.
Today I'm going to show you how to install these tools into your tailnet:
- golink - a URL shortener at
http://go - tmemes - an internal meme generator you can host at
http://memegen - tclip - a pastebin you can host at
http://paste
These tools help you augment your tailnet by giving you tools that will make you and your team's life a lot easier. A URL shortener helps you link to complicated Google Docs URLs. A meme generator gives you a new innovative way to let off steam. A pastebin lets you share text with your team without having to worry about the service you're using going offline due to no fault of your own.
golink
To install golink, we need to add the golink binary to the configuration. You can do this with gok add:
gok add github.com/tailscale/golink/cmd/golink
Then configure it with gok edit:
{
// ...
"PackageConfig": {
// ...
"github.com/tailscale/golink/cmd/golink": {
"CommandLineFlags": [
"--sqlitedb=/perm/home/golink/data.db"
],
"Environment": [
// the same one from before
"TS_AUTHKEY=tskey-auth-hunter2-hunter2hunter2hunter2"
],
// don't start the service until NTP catches up
"WaitForClock": true
},
// ...
}
// ...
}
And finally push it with gok update:
gok update -i casa
It'll build the image, push it out over Tailscale, trigger a reboot, and be back up in the span of a minute. Once it's back up, you can open the web UI again and see the status of your golink instance at http://casa/status?path=%2fuser%2fgolink:
And then you can start using short URLs at http://go:
And that's it! You now have a super minimal VM running small programs that let you do useful things to you. You can add more programs to your configuration file and push them with gok update to add more functionality to your machine. You can even add your own programs to the configuration file and push them to your machine. It's a very simple system, but it's very powerful.
tmemes
Google is infamous for having an internal service named memegen. This allows Googlers to make internal-facing memes about the slings and arrows that impact them as highly paid programmers. This is an internal service inside Google that has a lot of serious investment of time and energy to make it the best possible experience it can be. It's to the point that reportedly people can keep up with how an all-hands meeting is going by the tone of the sarcastic memes that are being posted to memegen.
The main reason this is run inside Google is to avoid information leaking via memes. Yes, this is an actual threat model.
Thanks to the magic of Tailscale, you can make your own private memegen using tmemes. tmemes is a tailnet addon that lets you post image macro templates and layer wisdom over it in the form of text.
Here's an example meme:
To add tmemes to your gokrazy machine, you can use gok add:
gok add github.com/tailscale/tmemes/tmemes
Then open your config with gok edit and add the following to your PackageConfig block:
{
// ...
"PackageConfig": {
// ...
"github.com/tailscale/tmemes/tmemes": {
"Environment": [
"TS_AUTHKEY=tskey-auth-hunter2-hunter2hunter2hunter2"
],
"CommandLineFlags": [
// change this to your desired hostname
"--hostname=memegen",
// change this to your username on Tailscale
"--admin=Xe@github",
"--store=/perm/home/tmemes"
],
"WaitForClock": true
},
// ...
},
// ...
}
And then push it with gok update:
gok update -i casa
Then you can head to http://memegen and upload a template to make your own dank memes.
If you want to integrate your own tools with tmemes, you can check out the API documentation. This should help you do whatever it is you want with a meme generator as a service.
tclip
Sometimes you just need a place to paste text and get a URL pointing to it. tclip is a tool that you can add to your tailnet and get exactly that. It's a very simple tool, but it's very useful. It's also written in Go, so it's a perfect fit for gokrazy. Their recent update to remove Cgo dependencies makes it possible to run your tclip node on a gokrazy machine.
To add tclip to your gokrazy machine, you can use gok add:
gok add github.com/tailscale-dev/tclip/cmd/tclipd
Then open your config with gok edit and add the following to your PackageConfig block:
{
// ...
"PackageConfig": {
// ...
"github.com/tailscale-dev/tclip/cmd/tclipd": {
"CommandLineFlags": [
"--data-location=/perm/home/tclip/"
],
"WaitForClock": true,
"Environment": [
"TS_AUTHKEY=tskey-auth-hunter2-hunter2hunter2hunter2",
"USE_FUNNEL=true" // Remove this if you don't want to use Funnel
]
},
// ...
}
}
And then push it with gok update:
gok update -i casa
And then you can start using it by heading to http://paste. Install the command-line tool on your development workstation with go install:
go install github.com/tailscale-dev/tclip/cmd/tclip@latest
Here's an example tclip link if you want to see what it looks like in practice: interjection.c. It's a very simple tool, but it's very useful.
Conclusion
gokrazy is insanely cool. It's the easiest way to deploy Go services to your homelab. It integrates seamlessly with Tailscale, and is something that I'm very excited to see grow and mature. I'm very excited to see what the future holds for gokrazy, and I'm very excited to see what people do with it.
I've seen signs that they're going to be adding an automatic update process, and that has me very excited. I'm also excited to see what other services people add to the gokrazy ecosystem. I'm hoping to add a few of my own in the future, and I'm hoping to see what other people do with it.



